diff --git a/libnymea-app/devicemanager.cpp b/libnymea-app/devicemanager.cpp index bbb77a9e..8d9ba643 100644 --- a/libnymea-app/devicemanager.cpp +++ b/libnymea-app/devicemanager.cpp @@ -126,6 +126,11 @@ Devices *DeviceManager::devices() const return m_devices; } +Devices *DeviceManager::things() const +{ + return m_devices; +} + DeviceClasses *DeviceManager::deviceClasses() const { return m_deviceClasses; diff --git a/libnymea-app/devicemanager.h b/libnymea-app/devicemanager.h index ef602cb0..872bd480 100644 --- a/libnymea-app/devicemanager.h +++ b/libnymea-app/devicemanager.h @@ -54,6 +54,7 @@ class DeviceManager : public JsonHandler Q_OBJECT Q_PROPERTY(Vendors* vendors READ vendors CONSTANT) Q_PROPERTY(Plugins* plugins READ plugins CONSTANT) + Q_PROPERTY(Devices* things READ things CONSTANT) Q_PROPERTY(Devices* devices READ devices CONSTANT) Q_PROPERTY(DeviceClasses* deviceClasses READ deviceClasses CONSTANT) Q_PROPERTY(IOConnections* ioConnections READ ioConnections CONSTANT) @@ -78,6 +79,7 @@ public: Vendors* vendors() const; Plugins* plugins() const; Devices* devices() const; + Devices* things() const; DeviceClasses* deviceClasses() const; IOConnections* ioConnections() const; diff --git a/libnymea-app/jsonrpc/jsontypes.cpp b/libnymea-app/jsonrpc/jsontypes.cpp index 61d60de6..a890a807 100644 --- a/libnymea-app/jsonrpc/jsontypes.cpp +++ b/libnymea-app/jsonrpc/jsontypes.cpp @@ -308,116 +308,8 @@ Device* JsonTypes::unpackDevice(DeviceManager *deviceManager, const QVariantMap return device; } -QVariantMap JsonTypes::packRule(Rule *rule) -{ - QVariantMap ret; - if (!rule->id().isNull()) { - ret.insert("ruleId", rule->id()); - } - ret.insert("name", rule->name()); - ret.insert("enabled", rule->enabled()); - ret.insert("executable", rule->executable()); - if (rule->actions()->rowCount() > 0) { - ret.insert("actions", packRuleActions(rule->actions())); - } - if (rule->exitActions()->rowCount() > 0) { - ret.insert("exitActions", packRuleActions(rule->exitActions())); - } - if (rule->eventDescriptors()->rowCount() > 0) { - ret.insert("eventDescriptors", packEventDescriptors(rule->eventDescriptors())); - } - - if (rule->timeDescriptor()->timeEventItems()->rowCount() > 0 || rule->timeDescriptor()->calendarItems()->rowCount() > 0) { - ret.insert("timeDescriptor", packTimeDescriptor(rule->timeDescriptor())); - } - - if (rule->stateEvaluator()) { - ret.insert("stateEvaluator", packStateEvaluator(rule->stateEvaluator())); - } - - return ret; -} - -QVariantList JsonTypes::packRuleActions(RuleActions *ruleActions) -{ - QVariantList ret; - for (int i = 0; i < ruleActions->rowCount(); i++) { - QVariantMap ruleAction; - RuleAction *ra = ruleActions->get(i); - if (!ra->actionTypeId().isNull() && !ra->deviceId().isNull()) { - ruleAction.insert("deviceId", ra->deviceId()); - ruleAction.insert("actionTypeId", ra->actionTypeId()); - } else if (!ra->deviceId().isNull() && !ra->browserItemId().isEmpty()) { - ruleAction.insert("deviceId", ra->deviceId()); - ruleAction.insert("browserItemId", ra->browserItemId()); - } else { - ruleAction.insert("interface", ra->interfaceName()); - ruleAction.insert("interfaceAction", ra->interfaceAction()); - } - if (ra->ruleActionParams()->rowCount() > 0) { - QVariantList ruleActionParams; - for (int j = 0; j < ra->ruleActionParams()->rowCount(); j++) { - QVariantMap ruleActionParam; - RuleActionParam *rap = ruleActions->get(i)->ruleActionParams()->get(j); - if (!rap->paramTypeId().isNull()) { - ruleActionParam.insert("paramTypeId", rap->paramTypeId()); - } else { - ruleActionParam.insert("paramName", rap->paramName()); - } - if (rap->isValueBased()) { - ruleActionParam.insert("value", rap->value()); - } else if (rap->isEventParamBased()) { - ruleActionParam.insert("eventTypeId", rap->eventTypeId()); - ruleActionParam.insert("eventParamTypeId", rap->eventParamTypeId()); - } else { - ruleActionParam.insert("stateDeviceId", rap->stateDeviceId()); - ruleActionParam.insert("stateTypeId", rap->stateTypeId()); - } - ruleActionParams.append(ruleActionParam); - } - ruleAction.insert("ruleActionParams", ruleActionParams); - } - ret.append(ruleAction); - } - - return ret; -} - -QVariantList JsonTypes::packEventDescriptors(EventDescriptors *eventDescriptors) -{ - QVariantList ret; - for (int i = 0; i < eventDescriptors->rowCount(); i++) { - QVariantMap eventDescriptorMap; - EventDescriptor* eventDescriptor = eventDescriptors->get(i); - if (!eventDescriptor->deviceId().isNull() && !eventDescriptor->eventTypeId().isNull()) { - eventDescriptorMap.insert("eventTypeId", eventDescriptor->eventTypeId()); - eventDescriptorMap.insert("deviceId", eventDescriptor->deviceId()); - } else { - eventDescriptorMap.insert("interface", eventDescriptor->interfaceName()); - eventDescriptorMap.insert("interfaceEvent", eventDescriptor->interfaceEvent()); - } - if (eventDescriptor->paramDescriptors()->rowCount() > 0) { - QVariantList paramDescriptors; - for (int j = 0; j < eventDescriptor->paramDescriptors()->rowCount(); j++) { - QVariantMap paramDescriptor; - if (!eventDescriptor->paramDescriptors()->get(j)->paramTypeId().isEmpty()) { - paramDescriptor.insert("paramTypeId", eventDescriptor->paramDescriptors()->get(j)->paramTypeId()); - } else { - paramDescriptor.insert("paramName", eventDescriptor->paramDescriptors()->get(j)->paramName()); - } - paramDescriptor.insert("value", eventDescriptor->paramDescriptors()->get(j)->value()); - QMetaEnum operatorEnum = QMetaEnum::fromType(); - paramDescriptor.insert("operator", operatorEnum.valueToKey(eventDescriptor->paramDescriptors()->get(j)->operatorType())); - paramDescriptors.append(paramDescriptor); - } - eventDescriptorMap.insert("paramDescriptors", paramDescriptors); - } - ret.append(eventDescriptorMap); - } - return ret; -} QVariantMap JsonTypes::packParam(Param *param) { @@ -427,92 +319,6 @@ QVariantMap JsonTypes::packParam(Param *param) return ret; } -QVariantMap JsonTypes::packStateEvaluator(StateEvaluator *stateEvaluator) -{ - QVariantMap ret; - QMetaEnum stateOperatorEnum = QMetaEnum::fromType(); - ret.insert("operator", stateOperatorEnum.valueToKey(stateEvaluator->stateOperator())); - QVariantMap stateDescriptor; - if (!stateEvaluator->stateDescriptor()->deviceId().isNull() && !stateEvaluator->stateDescriptor()->stateTypeId().isNull()) { - stateDescriptor.insert("deviceId", stateEvaluator->stateDescriptor()->deviceId()); - stateDescriptor.insert("stateTypeId", stateEvaluator->stateDescriptor()->stateTypeId()); - } else { - stateDescriptor.insert("interface", stateEvaluator->stateDescriptor()->interfaceName()); - stateDescriptor.insert("interfaceState", stateEvaluator->stateDescriptor()->interfaceState()); - } - QMetaEnum valueOperatorEnum = QMetaEnum::fromType(); - stateDescriptor.insert("operator", valueOperatorEnum.valueToKeys(stateEvaluator->stateDescriptor()->valueOperator())); - stateDescriptor.insert("value", stateEvaluator->stateDescriptor()->value()); - ret.insert("stateDescriptor", stateDescriptor); - QVariantList childEvaluators; - for (int i = 0; i < stateEvaluator->childEvaluators()->rowCount(); i++) { - childEvaluators.append(packStateEvaluator(stateEvaluator->childEvaluators()->get(i))); - } - ret.insert("childEvaluators", childEvaluators); - return ret; -} - -QVariantMap JsonTypes::packTimeDescriptor(TimeDescriptor *timeDescriptor) -{ - QVariantMap ret; - QVariantList timeEventItems; - for (int i = 0; i < timeDescriptor->timeEventItems()->rowCount(); i++) { - timeEventItems.append(packTimeEventItem(timeDescriptor->timeEventItems()->get(i))); - } - if (!timeEventItems.isEmpty()) { - ret.insert("timeEventItems", timeEventItems); - } - QVariantList calendarItems; - for (int i = 0; i < timeDescriptor->calendarItems()->rowCount(); i++) { - calendarItems.append(packCalendarItem(timeDescriptor->calendarItems()->get(i))); - } - if (!calendarItems.isEmpty()) { - ret.insert("calendarItems", calendarItems); - } - return ret; -} - -QVariantMap JsonTypes::packTimeEventItem(TimeEventItem *timeEventItem) -{ - QVariantMap ret; - if (!timeEventItem->time().isNull()) { - ret.insert("time", timeEventItem->time().toString("hh:mm")); - } - if (!timeEventItem->dateTime().isNull()) { - ret.insert("datetime", timeEventItem->dateTime().toSecsSinceEpoch()); - } - ret.insert("repeating", packRepeatingOption(timeEventItem->repeatingOption())); - return ret; -} - -QVariantMap JsonTypes::packCalendarItem(CalendarItem *calendarItem) -{ - QVariantMap ret; - ret.insert("duration", calendarItem->duration()); - if (!calendarItem->dateTime().isNull()) { - ret.insert("datetime", calendarItem->dateTime().toSecsSinceEpoch()); - } - if (!calendarItem->startTime().isNull()) { - ret.insert("startTime", calendarItem->startTime().toString("hh:mm")); - } - ret.insert("repeating", packRepeatingOption(calendarItem->repeatingOption())); - return ret; -} - -QVariantMap JsonTypes::packRepeatingOption(RepeatingOption *repeatingOption) -{ - QVariantMap ret; - QMetaEnum repeatingModeEnum = QMetaEnum::fromType(); - ret.insert("mode", repeatingModeEnum.valueToKey(repeatingOption->repeatingMode())); - if (!repeatingOption->weekDays().isEmpty()) { - ret.insert("weekDays", repeatingOption->weekDays()); - } - if (!repeatingOption->monthDays().isEmpty()) { - ret.insert("monthDays", repeatingOption->monthDays()); - } - return ret; -} - DeviceClass::SetupMethod JsonTypes::stringToSetupMethod(const QString &setupMethodString) { if (setupMethodString == "SetupMethodJustAdd") { diff --git a/libnymea-app/jsonrpc/jsontypes.h b/libnymea-app/jsonrpc/jsontypes.h index c963ad84..736fac32 100644 --- a/libnymea-app/jsonrpc/jsontypes.h +++ b/libnymea-app/jsonrpc/jsontypes.h @@ -51,14 +51,6 @@ class DeviceManager; class Device; class DeviceClasses; class Param; -class Rule; -class StateEvaluator; -class RuleActions; -class EventDescriptors; -class TimeDescriptor; -class TimeEventItem; -class CalendarItem; -class RepeatingOption; class JsonTypes : public QObject { @@ -76,15 +68,7 @@ public: static ActionType *unpackActionType(const QVariantMap &actionTypeMap, QObject *parent); static Device *unpackDevice(DeviceManager *deviceManager, const QVariantMap &deviceMap, DeviceClasses *deviceClasses, Device *oldDevice = nullptr); - static QVariantMap packRule(Rule* rule); - static QVariantList packRuleActions(RuleActions* ruleActions); - static QVariantList packEventDescriptors(EventDescriptors* eventDescriptors); static QVariantMap packParam(Param *param); - static QVariantMap packStateEvaluator(StateEvaluator* stateEvaluator); - static QVariantMap packTimeDescriptor(TimeDescriptor* timeDescriptor); - static QVariantMap packTimeEventItem(TimeEventItem* timeEventItem); - static QVariantMap packCalendarItem(CalendarItem* calendarItem); - static QVariantMap packRepeatingOption(RepeatingOption* repeatingOption); private: static DeviceClass::SetupMethod stringToSetupMethod(const QString &setupMethodString); diff --git a/libnymea-app/libnymea-app-core.h b/libnymea-app/libnymea-app-core.h index 7890f84e..1b6b0117 100644 --- a/libnymea-app/libnymea-app-core.h +++ b/libnymea-app/libnymea-app-core.h @@ -83,6 +83,9 @@ #include "ruletemplates/ruletemplates.h" #include "ruletemplates/ruletemplate.h" #include "ruletemplates/eventdescriptortemplate.h" +#include "ruletemplates/timedescriptortemplate.h" +#include "ruletemplates/calendaritemtemplate.h" +#include "ruletemplates/timeeventitemtemplate.h" #include "ruletemplates/stateevaluatortemplate.h" #include "ruletemplates/statedescriptortemplate.h" #include "ruletemplates/ruleactiontemplate.h" @@ -265,6 +268,11 @@ void registerQmlTypes() { qmlRegisterUncreatableType(uri, 1, 0, "RuleTemplate", "Get them from RuleTemplates"); qmlRegisterUncreatableType(uri, 1, 0, "EventDescriptorTemplates", "Get it from RuleTemplate"); qmlRegisterUncreatableType(uri, 1, 0, "EventDescriptorTemplate", "Get it from EventDescriptorTemplates"); + qmlRegisterUncreatableType(uri, 1, 0, "TimeDescriptorTemplate", "Get it from RuleTemplate"); + qmlRegisterUncreatableType(uri, 1, 0, "CalendarItemTemplates", "Get it from TimeDescriptorTemplate"); + qmlRegisterUncreatableType(uri, 1, 0, "CalendarItemTemplate", "Get it from CalendarItemTemplates"); + qmlRegisterUncreatableType(uri, 1, 0, "TimeEventItemTemplates", "Get it from TimeDescriptorTemplate"); + qmlRegisterUncreatableType(uri, 1, 0, "TimeEventItemTemplate", "Get it from TimeEventItemTemplates"); qmlRegisterUncreatableType(uri, 1, 0, "StateEvaluatorTemplate", "Get it from RuleTemplate"); qmlRegisterUncreatableType(uri, 1, 0, "StateDescriptorTemplate", "Get it from StateEvaluatorTemplate"); qmlRegisterUncreatableType(uri, 1, 0, "RuleActionTemplates", "Get it from RuleTemplate"); diff --git a/libnymea-app/libnymea-app.pro b/libnymea-app/libnymea-app.pro index c8217466..8e3e1925 100644 --- a/libnymea-app/libnymea-app.pro +++ b/libnymea-app/libnymea-app.pro @@ -27,6 +27,9 @@ INCLUDEPATH += $$top_srcdir/QtZeroConf SOURCES += \ configuration/networkmanager.cpp \ engine.cpp \ + ruletemplates/calendaritemtemplate.cpp \ + ruletemplates/timedescriptortemplate.cpp \ + ruletemplates/timeeventitemtemplate.cpp \ types/browseritem.cpp \ types/browseritems.cpp \ types/networkdevice.cpp \ @@ -159,6 +162,9 @@ SOURCES += \ HEADERS += \ configuration/networkmanager.h \ engine.h \ + ruletemplates/calendaritemtemplate.h \ + ruletemplates/timedescriptortemplate.h \ + ruletemplates/timeeventitemtemplate.h \ types/browseritem.h \ types/browseritems.h \ types/networkdevice.h \ diff --git a/libnymea-app/models/logsmodel.cpp b/libnymea-app/models/logsmodel.cpp index f88c6288..53ea9918 100644 --- a/libnymea-app/models/logsmodel.cpp +++ b/libnymea-app/models/logsmodel.cpp @@ -71,8 +71,8 @@ QVariant LogsModel::data(const QModelIndex &index, int role) const return m_list.at(index.row())->timestamp(); case RoleValue: return m_list.at(index.row())->value(); - case RoleDeviceId: - return m_list.at(index.row())->deviceId(); + case RoleThingId: + return m_list.at(index.row())->thingId(); case RoleTypeId: return m_list.at(index.row())->typeId(); case RoleSource: @@ -88,7 +88,7 @@ QHash LogsModel::roleNames() const QHash roles; roles.insert(RoleTimestamp, "timestamp"); roles.insert(RoleValue, "value"); - roles.insert(RoleDeviceId, "deviceId"); + roles.insert(RoleThingId, "deviceId"); roles.insert(RoleTypeId, "typeId"); roles.insert(RoleSource, "source"); roles.insert(RoleLoggingEventType, "loggingEventType"); diff --git a/libnymea-app/models/logsmodel.h b/libnymea-app/models/logsmodel.h index 94c31154..ac017c06 100644 --- a/libnymea-app/models/logsmodel.h +++ b/libnymea-app/models/logsmodel.h @@ -57,7 +57,8 @@ public: enum Roles { RoleTimestamp, RoleValue, - RoleDeviceId, + RoleThingId, + RoleDeviceId, // < JSONRPC 5.0 RoleTypeId, RoleSource, RoleLoggingEventType diff --git a/libnymea-app/models/logsmodelng.cpp b/libnymea-app/models/logsmodelng.cpp index bd0ad322..3ea664be 100644 --- a/libnymea-app/models/logsmodelng.cpp +++ b/libnymea-app/models/logsmodelng.cpp @@ -70,8 +70,9 @@ QVariant LogsModelNg::data(const QModelIndex &index, int role) const return m_list.at(index.row())->timestamp(); case RoleValue: return m_list.at(index.row())->value(); + case RoleThingId: case RoleDeviceId: - return m_list.at(index.row())->deviceId(); + return m_list.at(index.row())->thingId(); case RoleTypeId: return m_list.at(index.row())->typeId(); case RoleSource: @@ -87,6 +88,7 @@ QHash LogsModelNg::roleNames() const QHash roles; roles.insert(RoleTimestamp, "timestamp"); roles.insert(RoleValue, "value"); + roles.insert(RoleThingId, "thingId"); roles.insert(RoleDeviceId, "deviceId"); roles.insert(RoleTypeId, "typeId"); roles.insert(RoleSource, "source"); @@ -112,16 +114,16 @@ void LogsModelNg::setLive(bool live) } } -QUuid LogsModelNg::deviceId() const +QUuid LogsModelNg::thingId() const { - return m_deviceId; + return m_thingId; } -void LogsModelNg::setDeviceId(const QUuid &deviceId) +void LogsModelNg::setThingId(const QUuid &thingId) { - if (m_deviceId != deviceId) { - m_deviceId = deviceId; - emit deviceIdChanged(); + if (m_thingId != thingId) { + m_thingId = thingId; + emit thingIdChanged(); } } @@ -236,14 +238,19 @@ void LogsModelNg::logsReply(const QVariantMap &data) foreach (const QVariant &logEntryVariant, logEntries) { QVariantMap entryMap = logEntryVariant.toMap(); QDateTime timeStamp = QDateTime::fromMSecsSinceEpoch(entryMap.value("timestamp").toLongLong()); - QString deviceId = entryMap.value("deviceId").toString(); + QString thingId; + if (m_engine->jsonRpcClient()->ensureServerVersion("5.0")) { + thingId = entryMap.value("thingId").toString(); + } else { + thingId = entryMap.value("deviceId").toString(); + } QString typeId = entryMap.value("typeId").toString(); QMetaEnum sourceEnum = QMetaEnum::fromType(); LogEntry::LoggingSource loggingSource = static_cast(sourceEnum.keyToValue(entryMap.value("source").toByteArray())); QMetaEnum loggingEventTypeEnum = QMetaEnum::fromType(); LogEntry::LoggingEventType loggingEventType = static_cast(loggingEventTypeEnum.keyToValue(entryMap.value("eventType").toByteArray())); QVariant value = loggingEventType == LogEntry::LoggingEventTypeActiveChange ? entryMap.value("active").toBool() : entryMap.value("value"); - LogEntry *entry = new LogEntry(timeStamp, value, deviceId, typeId, loggingSource, loggingEventType, this); + LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, this); newBlock.append(entry); } @@ -265,7 +272,7 @@ void LogsModelNg::logsReply(const QVariantMap &data) for (int i = 0; i < newBlock.count(); i++) { LogEntry *entry = newBlock.at(i); m_list.insert(offset + i, entry); - Device *dev = m_engine->deviceManager()->devices()->getDevice(entry->deviceId()); + Device *dev = m_engine->deviceManager()->devices()->getDevice(entry->thingId()); if (!dev) { qWarning() << "Device not found in system. Cannot add item to graph series."; continue; @@ -369,10 +376,14 @@ void LogsModelNg::fetchMore(const QModelIndex &parent) emit busyChanged(); QVariantMap params; - if (!m_deviceId.isNull()) { - QVariantList deviceIds; - deviceIds.append(m_deviceId); - params.insert("deviceIds", deviceIds); + if (!m_thingId.isNull()) { + QVariantList thingIds; + thingIds.append(m_thingId); + if (m_engine->jsonRpcClient()->ensureServerVersion("5.0")) { + params.insert("thingIds", thingIds); + } else { + params.insert("deviceIds", thingIds); + } } if (!m_typeIds.isEmpty()) { QVariantList typeIds; @@ -414,8 +425,13 @@ void LogsModelNg::newLogEntryReceived(const QVariantMap &data) } QVariantMap entryMap = data; - QUuid deviceId = entryMap.value("deviceId").toUuid(); - if (!m_deviceId.isNull() && deviceId != m_deviceId) { + QUuid thingId; + if (m_engine->jsonRpcClient()->ensureServerVersion("5.0")) { + thingId = entryMap.value("deviceId").toUuid(); + } else { + thingId = entryMap.value("thingId").toUuid(); + } + if (!m_thingId.isNull() && thingId != m_thingId) { return; } @@ -431,11 +447,11 @@ void LogsModelNg::newLogEntryReceived(const QVariantMap &data) QMetaEnum loggingEventTypeEnum = QMetaEnum::fromType(); LogEntry::LoggingEventType loggingEventType = static_cast(loggingEventTypeEnum.keyToValue(entryMap.value("eventType").toByteArray())); QVariant value = loggingEventType == LogEntry::LoggingEventTypeActiveChange ? entryMap.value("active").toBool() : entryMap.value("value"); - LogEntry *entry = new LogEntry(timeStamp, value, deviceId, typeId, loggingSource, loggingEventType, this); + LogEntry *entry = new LogEntry(timeStamp, value, thingId, typeId, loggingSource, loggingEventType, this); m_list.prepend(entry); if (m_graphSeries) { - Device *dev = m_engine->deviceManager()->devices()->getDevice(entry->deviceId()); + Device *dev = m_engine->thingManager()->devices()->getDevice(entry->thingId()); StateType *entryStateType = dev->deviceClass()->stateTypes()->getStateType(entry->typeId()); diff --git a/libnymea-app/models/logsmodelng.h b/libnymea-app/models/logsmodelng.h index d97c40ec..a2a06776 100644 --- a/libnymea-app/models/logsmodelng.h +++ b/libnymea-app/models/logsmodelng.h @@ -47,7 +47,8 @@ class LogsModelNg : public QAbstractListModel Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged) Q_PROPERTY(int count READ rowCount NOTIFY countChanged) - Q_PROPERTY(QUuid deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) + Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId NOTIFY thingIdChanged) + Q_PROPERTY(QUuid deviceId READ thingId WRITE setThingId NOTIFY thingIdChanged) Q_PROPERTY(QStringList typeIds READ typeIds WRITE setTypeIds NOTIFY typeIdsChanged) Q_PROPERTY(QDateTime startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) Q_PROPERTY(QDateTime endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged) @@ -61,7 +62,8 @@ public: enum Roles { RoleTimestamp, RoleValue, - RoleDeviceId, + RoleThingId, + RoleDeviceId, // < JSONRPC 5.0 RoleTypeId, RoleSource, RoleLoggingEventType @@ -81,8 +83,8 @@ public: bool live() const; void setLive(bool live); - QUuid deviceId() const; - void setDeviceId(const QUuid &deviceId); + QUuid thingId() const; + void setThingId(const QUuid &thingId); QStringList typeIds() const; void setTypeIds(const QStringList &typeId); @@ -111,7 +113,7 @@ protected: signals: void busyChanged(); void liveChanged(); - void deviceIdChanged(); + void thingIdChanged(); void typeIdsChanged(); void countChanged(); void startTimeChanged(); @@ -132,7 +134,7 @@ private: Engine *m_engine = nullptr; bool m_busy = false; bool m_live = false; - QUuid m_deviceId; + QUuid m_thingId; QList m_typeIds; QDateTime m_startTime; QDateTime m_endTime; diff --git a/libnymea-app/models/rulesfiltermodel.cpp b/libnymea-app/models/rulesfiltermodel.cpp index 9e89ab12..3331ef19 100644 --- a/libnymea-app/models/rulesfiltermodel.cpp +++ b/libnymea-app/models/rulesfiltermodel.cpp @@ -61,16 +61,16 @@ void RulesFilterModel::setRules(Rules *rules) } } -QString RulesFilterModel::filterDeviceId() const +QUuid RulesFilterModel::filterThingId() const { - return m_filterDeviceId; + return m_filterThingId; } -void RulesFilterModel::setFilterDeviceId(const QString &filterDeviceId) +void RulesFilterModel::setFilterThingId(const QUuid &filterThingId) { - if (m_filterDeviceId != filterDeviceId) { - m_filterDeviceId = filterDeviceId; - emit filterDeviceIdChanged(); + if (m_filterThingId != filterThingId) { + m_filterThingId = filterThingId; + emit filterThingIdChanged(); invalidateFilter(); emit countChanged(); } @@ -104,22 +104,22 @@ bool RulesFilterModel::filterAcceptsRow(int source_row, const QModelIndex &sourc return false; } bool found = true; - if (!m_filterDeviceId.isNull()) { + if (!m_filterThingId.isNull()) { found = false; for (int i = 0; i < rule->eventDescriptors()->rowCount(); i++) { EventDescriptor *ed = rule->eventDescriptors()->get(i); - if (ed->deviceId() == m_filterDeviceId) { + if (ed->thingId() == m_filterThingId) { found = true; break; } } - if (!found && rule->stateEvaluator() && rule->stateEvaluator()->containsDevice(m_filterDeviceId)) { + if (!found && rule->stateEvaluator() && rule->stateEvaluator()->containsDevice(m_filterThingId)) { found = true; } if (!found) { for (int i = 0; i < rule->actions()->rowCount(); i++) { RuleAction *ra = rule->actions()->get(i); - if (ra->deviceId() == m_filterDeviceId) { + if (ra->deviceId() == m_filterThingId) { found = true; break; } @@ -128,7 +128,7 @@ bool RulesFilterModel::filterAcceptsRow(int source_row, const QModelIndex &sourc if (!found) { for (int i = 0; i < rule->exitActions()->rowCount(); i++) { RuleAction *ra = rule->exitActions()->get(i); - if (ra->deviceId() == m_filterDeviceId) { + if (ra->deviceId() == m_filterThingId) { found = true; break; } diff --git a/libnymea-app/models/rulesfiltermodel.h b/libnymea-app/models/rulesfiltermodel.h index ce9848c9..7c081066 100644 --- a/libnymea-app/models/rulesfiltermodel.h +++ b/libnymea-app/models/rulesfiltermodel.h @@ -42,7 +42,8 @@ class RulesFilterModel : public QSortFilterProxyModel Q_OBJECT Q_PROPERTY(int count READ rowCount NOTIFY countChanged) Q_PROPERTY(Rules* rules READ rules WRITE setRules NOTIFY rulesChanged) - Q_PROPERTY(QString filterDeviceId READ filterDeviceId WRITE setFilterDeviceId NOTIFY filterDeviceIdChanged) + Q_PROPERTY(QUuid filterThingId READ filterThingId WRITE setFilterThingId NOTIFY filterThingIdChanged) + Q_PROPERTY(QUuid filterDeviceId READ filterThingId WRITE setFilterThingId NOTIFY filterThingIdChanged) Q_PROPERTY(bool filterExecutable READ filterExecutable WRITE setFilterExecutable NOTIFY filterExecutableChanged) public: @@ -51,8 +52,8 @@ public: Rules* rules() const; void setRules(Rules* rules); - QString filterDeviceId() const; - void setFilterDeviceId(const QString &filterDeviceId); + QUuid filterThingId() const; + void setFilterThingId(const QUuid &filterThingId); bool filterExecutable() const; void setFilterExecutable(bool filterExecutable); @@ -61,7 +62,7 @@ public: signals: void rulesChanged(); - void filterDeviceIdChanged(); + void filterThingIdChanged(); void filterExecutableChanged(); void countChanged(); @@ -70,7 +71,7 @@ protected: private: Rules *m_rules = nullptr; - QString m_filterDeviceId; + QUuid m_filterThingId; bool m_filterExecutable = false; }; diff --git a/libnymea-app/models/tagsproxymodel.cpp b/libnymea-app/models/tagsproxymodel.cpp index d5ec281e..9cd4fd5c 100644 --- a/libnymea-app/models/tagsproxymodel.cpp +++ b/libnymea-app/models/tagsproxymodel.cpp @@ -47,7 +47,11 @@ void TagsProxyModel::setTags(Tags *tags) if (m_tags != tags) { m_tags = tags; setSourceModel(tags); - connect(tags, &Tags::countChanged, this, &TagsProxyModel::countChanged, Qt::QueuedConnection); + connect(tags, &Tags::countChanged, this, [=](){ + qWarning() << "Tag count changed!"; + invalidateFilter(); + emit countChanged(); + }, Qt::QueuedConnection); setSortRole(Tags::RoleValue); sort(0); emit tagsChanged(); @@ -70,27 +74,27 @@ void TagsProxyModel::setFilterTagId(const QString &filterTagId) } } -QString TagsProxyModel::filterDeviceId() const +QUuid TagsProxyModel::filterThingId() const { - return m_filterDeviceId; + return m_filterThingId; } -void TagsProxyModel::setFilterDeviceId(const QString &filterDeviceId) +void TagsProxyModel::setFilterThingId(const QUuid &filterThingId) { - if (m_filterDeviceId != filterDeviceId) { - m_filterDeviceId = filterDeviceId; - emit filterDeviceIdChanged(); + if (m_filterThingId != filterThingId) { + m_filterThingId = filterThingId; + emit filterThingIdChanged(); invalidateFilter(); emit countChanged(); } } -QString TagsProxyModel::filterRuleId() const +QUuid TagsProxyModel::filterRuleId() const { return m_filterRuleId; } -void TagsProxyModel::setFilterRuleId(const QString &filterRuleId) +void TagsProxyModel::setFilterRuleId(const QUuid &filterRuleId) { if (m_filterRuleId != filterRuleId) { m_filterRuleId = filterRuleId; @@ -100,6 +104,21 @@ void TagsProxyModel::setFilterRuleId(const QString &filterRuleId) } } +QString TagsProxyModel::filterValue() const +{ + return m_filterValue; +} + +void TagsProxyModel::setFilterValue(const QString &filterValue) +{ + if (m_filterValue != filterValue) { + m_filterValue = filterValue; + emit filterValueChanged(); + invalidateFilter(); + emit countChanged(); + } +} + Tag *TagsProxyModel::get(int index) const { if (index < 0 || index > rowCount()) { @@ -129,13 +148,19 @@ bool TagsProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_ return false; } } - if (!m_filterDeviceId.isEmpty()) { - if (QUuid(tag->deviceId()) != QUuid(m_filterDeviceId)) { + if (!m_filterThingId.isNull()) { + if (tag->thingId() != m_filterThingId) { return false; } } - if (!m_filterRuleId.isEmpty()) { - if (QUuid(tag->ruleId()) != QUuid(m_filterRuleId)) { + if (!m_filterRuleId.isNull()) { + if (tag->ruleId() != m_filterRuleId) { + return false; + } + } + if (!m_filterValue.isEmpty()) { + qDebug() << "**************************************************************" << tag->value() << m_filterValue; + if (tag->value() != m_filterValue) { return false; } } diff --git a/libnymea-app/models/tagsproxymodel.h b/libnymea-app/models/tagsproxymodel.h index ced09794..f0ca7b1d 100644 --- a/libnymea-app/models/tagsproxymodel.h +++ b/libnymea-app/models/tagsproxymodel.h @@ -32,6 +32,7 @@ #define TAGSPROXYMODEL_H #include +#include class Tag; class Tags; @@ -42,8 +43,10 @@ class TagsProxyModel : public QSortFilterProxyModel Q_PROPERTY(Tags* tags READ tags WRITE setTags NOTIFY tagsChanged) Q_PROPERTY(int count READ rowCount NOTIFY countChanged) Q_PROPERTY(QString filterTagId READ filterTagId WRITE setFilterTagId NOTIFY filterTagIdChanged) - Q_PROPERTY(QString filterDeviceId READ filterDeviceId WRITE setFilterDeviceId NOTIFY filterDeviceIdChanged) - Q_PROPERTY(QString filterRuleId READ filterRuleId WRITE setFilterRuleId NOTIFY filterRuleIdChanged) + Q_PROPERTY(QUuid filterThingId READ filterThingId WRITE setFilterThingId NOTIFY filterThingIdChanged) + Q_PROPERTY(QUuid filterDeviceId READ filterThingId WRITE setFilterThingId NOTIFY filterThingIdChanged) + Q_PROPERTY(QUuid filterRuleId READ filterRuleId WRITE setFilterRuleId NOTIFY filterRuleIdChanged) + Q_PROPERTY(QString filterValue READ filterValue WRITE setFilterValue NOTIFY filterValueChanged) public: explicit TagsProxyModel(QObject *parent = nullptr); @@ -54,11 +57,14 @@ public: QString filterTagId() const; void setFilterTagId(const QString &filterTagId); - QString filterDeviceId() const; - void setFilterDeviceId(const QString &filterDeviceId); + QUuid filterThingId() const; + void setFilterThingId(const QUuid &filterThingId); - QString filterRuleId() const; - void setFilterRuleId(const QString &filterRuleId); + QUuid filterRuleId() const; + void setFilterRuleId(const QUuid &filterRuleId); + + QString filterValue() const; + void setFilterValue(const QString &filterValue); Q_INVOKABLE Tag* get(int index) const; Q_INVOKABLE Tag* findTag(const QString &tagId) const; @@ -70,16 +76,18 @@ protected: signals: void tagsChanged(); void filterTagIdChanged(); - void filterDeviceIdChanged(); + void filterThingIdChanged(); void filterRuleIdChanged(); + void filterValueChanged(); void groupSameTagsChanged(); void countChanged(); private: Tags *m_tags = nullptr; QString m_filterTagId; - QString m_filterDeviceId; - QString m_filterRuleId; + QUuid m_filterThingId; + QUuid m_filterRuleId; + QString m_filterValue; }; #endif // TAGSPROXYMODEL_H diff --git a/libnymea-app/rulemanager.cpp b/libnymea-app/rulemanager.cpp index 8c9f21b5..3755c069 100644 --- a/libnymea-app/rulemanager.cpp +++ b/libnymea-app/rulemanager.cpp @@ -84,16 +84,16 @@ Rule *RuleManager::createNewRule() return new Rule(QUuid(), this); } -void RuleManager::addRule(const QVariantMap params) +int RuleManager::addRule(const QVariantMap params) { - m_jsonClient->sendCommand("Rules.AddRule", params, this, "onAddRuleReply"); + return m_jsonClient->sendCommand("Rules.AddRule", params, this, "onAddRuleReply"); } -void RuleManager::addRule(Rule *rule) +int RuleManager::addRule(Rule *rule) { - QVariantMap params = JsonTypes::packRule(rule); + QVariantMap params = packRule(rule); qDebug() << "packed rule:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented)); - m_jsonClient->sendCommand("Rules.AddRule", params, this, "onAddRuleReply"); + return m_jsonClient->sendCommand("Rules.AddRule", params, this, "onAddRuleReply"); } void RuleManager::removeRule(const QUuid &ruleId) @@ -105,7 +105,7 @@ void RuleManager::removeRule(const QUuid &ruleId) void RuleManager::editRule(Rule *rule) { - QVariantMap params = JsonTypes::packRule(rule); + QVariantMap params = packRule(rule); qWarning() << "Packed rule:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson(QJsonDocument::Indented)); m_jsonClient->sendCommand("Rules.EditRule", params, this, "onEditRuleReply"); @@ -159,7 +159,7 @@ void RuleManager::getRulesReply(const QVariantMap ¶ms) qWarning() << "Error getting rules:" << params.value("error").toString(); return; } -// qDebug() << "Get Rules reply" << params; + // qDebug() << "Get Rules reply" << params; foreach (const QVariant &ruleDescriptionVariant, params.value("params").toMap().value("ruleDescriptions").toList()) { QUuid ruleId = ruleDescriptionVariant.toMap().value("id").toUuid(); QString name = ruleDescriptionVariant.toMap().value("name").toString(); @@ -193,14 +193,18 @@ void RuleManager::getRuleDetailsReply(const QVariantMap ¶ms) parseRuleExitActions(ruleMap.value("exitActions").toList(), rule); parseTimeDescriptor(ruleMap.value("timeDescriptor").toMap(), rule); rule->setStateEvaluator(parseStateEvaluator(ruleMap.value("stateEvaluator").toMap())); -// qDebug() << "** Rule details received:" << rule; -// qDebug() << "Rule JSON:" << qUtf8Printable(QJsonDocument::fromVariant(ruleMap).toJson()); + // qDebug() << "** Rule details received:" << rule; + // qDebug() << "Rule JSON:" << qUtf8Printable(QJsonDocument::fromVariant(ruleMap).toJson()); } void RuleManager::onAddRuleReply(const QVariantMap ¶ms) { - qDebug() << "Add rule reply:" << params;//.value("params").toMap().value("ruleError").toString(); - emit addRuleReply(params.value("params").toMap().value("ruleError").toString(), params.value("params").toMap().value("ruleId").toString()); + if (params.value("params").toMap().value("ruleError").toString() != "RuleErrorNoError") { + qWarning() << "Failed to add rule:" << qUtf8Printable(QJsonDocument::fromVariant(params).toJson()); + } else { + qDebug() << "Rule added successfully. Rule ID:" << params.value("params").toMap().value("ruleId").toString(); + } + emit addRuleReply(params.value("id").toInt(), params.value("params").toMap().value("ruleError").toString(), params.value("params").toMap().value("ruleId").toString()); } void RuleManager::removeRuleReply(const QVariantMap ¶ms) @@ -249,7 +253,11 @@ void RuleManager::parseEventDescriptors(const QVariantList &eventDescriptorList, { foreach (const QVariant &eventDescriptorVariant, eventDescriptorList) { EventDescriptor *eventDescriptor = new EventDescriptor(rule); - eventDescriptor->setDeviceId(eventDescriptorVariant.toMap().value("deviceId").toString()); + if (m_jsonClient->ensureServerVersion("5.0")) { + eventDescriptor->setThingId(eventDescriptorVariant.toMap().value("thingId").toString()); + } else { + eventDescriptor->setThingId(eventDescriptorVariant.toMap().value("deviceId").toString()); + } eventDescriptor->setEventTypeId(eventDescriptorVariant.toMap().value("eventTypeId").toString()); eventDescriptor->setInterfaceName(eventDescriptorVariant.toMap().value("interface").toString()); eventDescriptor->setInterfaceEvent(eventDescriptorVariant.toMap().value("interfaceEvent").toString()); @@ -262,14 +270,14 @@ void RuleManager::parseEventDescriptors(const QVariantList &eventDescriptorList, paramDescriptor->setOperatorType((ParamDescriptor::ValueOperator)operatorEnum.keyToValue(paramDescriptorVariant.toMap().value("operator").toString().toLocal8Bit())); eventDescriptor->paramDescriptors()->addParamDescriptor(paramDescriptor); } -// qDebug() << "Adding eventdescriptor" << eventDescriptor->deviceId() << eventDescriptor->eventTypeId(); + // qDebug() << "Adding eventdescriptor" << eventDescriptor->deviceId() << eventDescriptor->eventTypeId(); rule->eventDescriptors()->addEventDescriptor(eventDescriptor); } } StateEvaluator *RuleManager::parseStateEvaluator(const QVariantMap &stateEvaluatorMap) { -// qDebug() << "Parsing state evaluator. Child count:" << stateEvaluatorMap.value("childEvaluators").toList().count(); + // qDebug() << "Parsing state evaluator. Child count:" << stateEvaluatorMap.value("childEvaluators").toList().count(); if (!stateEvaluatorMap.contains("stateDescriptor")) { return nullptr; } @@ -280,7 +288,7 @@ StateEvaluator *RuleManager::parseStateEvaluator(const QVariantMap &stateEvaluat StateDescriptor *sd = nullptr; if (sdMap.contains("deviceId") && sdMap.contains("stateTypeId")) { - sd = new StateDescriptor(sdMap.value("deviceId").toUuid(), sdMap.value("stateTypeId").toUuid(), op, sdMap.value("value"), stateEvaluator); + sd = new StateDescriptor(sdMap.value("deviceId").toUuid(), sdMap.value("stateTypeId").toUuid(), op, sdMap.value("value"), stateEvaluator); } else { sd = new StateDescriptor(sdMap.value("interface").toString(), sdMap.value("interfaceState").toString(), op, sdMap.value("value"), stateEvaluator); } @@ -371,5 +379,206 @@ void RuleManager::parseTimeDescriptor(const QVariantMap &timeDescriptor, Rule *r calendarItem->repeatingOption()->setMonthDays(repeatingOptionMap.value("monthDays").toList()); rule->timeDescriptor()->calendarItems()->addCalendarItem(calendarItem); } -// rule->timeDescriptor() + // rule->timeDescriptor() +} + +QVariantMap RuleManager::packRule(Rule *rule) +{ + QVariantMap ret; + if (!rule->id().isNull()) { + ret.insert("ruleId", rule->id()); + } + ret.insert("name", rule->name()); + ret.insert("enabled", rule->enabled()); + ret.insert("executable", rule->executable()); + + if (rule->actions()->rowCount() > 0) { + ret.insert("actions", packRuleActions(rule->actions())); + } + if (rule->exitActions()->rowCount() > 0) { + ret.insert("exitActions", packRuleActions(rule->exitActions())); + } + + if (rule->eventDescriptors()->rowCount() > 0) { + ret.insert("eventDescriptors", packEventDescriptors(rule->eventDescriptors())); + } + + if (rule->timeDescriptor()->timeEventItems()->rowCount() > 0 || rule->timeDescriptor()->calendarItems()->rowCount() > 0) { + ret.insert("timeDescriptor", packTimeDescriptor(rule->timeDescriptor())); + } + + if (rule->stateEvaluator()) { + ret.insert("stateEvaluator", packStateEvaluator(rule->stateEvaluator())); + } + + return ret; +} + +QVariantList RuleManager::packEventDescriptors(EventDescriptors *eventDescriptors) +{ + QVariantList ret; + for (int i = 0; i < eventDescriptors->rowCount(); i++) { + QVariantMap eventDescriptorMap; + EventDescriptor* eventDescriptor = eventDescriptors->get(i); + if (!eventDescriptor->thingId().isNull() && !eventDescriptor->eventTypeId().isNull()) { + eventDescriptorMap.insert("eventTypeId", eventDescriptor->eventTypeId()); + if (m_jsonClient->ensureServerVersion("5.0")) { + eventDescriptorMap.insert("thingId", eventDescriptor->thingId()); + } else { + eventDescriptorMap.insert("deviceId", eventDescriptor->thingId()); + } + } else { + eventDescriptorMap.insert("interface", eventDescriptor->interfaceName()); + eventDescriptorMap.insert("interfaceEvent", eventDescriptor->interfaceEvent()); + } + if (eventDescriptor->paramDescriptors()->rowCount() > 0) { + QVariantList paramDescriptors; + for (int j = 0; j < eventDescriptor->paramDescriptors()->rowCount(); j++) { + QVariantMap paramDescriptor; + if (!eventDescriptor->paramDescriptors()->get(j)->paramTypeId().isEmpty()) { + paramDescriptor.insert("paramTypeId", eventDescriptor->paramDescriptors()->get(j)->paramTypeId()); + } else { + paramDescriptor.insert("paramName", eventDescriptor->paramDescriptors()->get(j)->paramName()); + } + paramDescriptor.insert("value", eventDescriptor->paramDescriptors()->get(j)->value()); + QMetaEnum operatorEnum = QMetaEnum::fromType(); + paramDescriptor.insert("operator", operatorEnum.valueToKey(eventDescriptor->paramDescriptors()->get(j)->operatorType())); + paramDescriptors.append(paramDescriptor); + } + eventDescriptorMap.insert("paramDescriptors", paramDescriptors); + } + ret.append(eventDescriptorMap); + } + return ret; +} + +QVariantMap RuleManager::packTimeDescriptor(TimeDescriptor *timeDescriptor) +{ + QVariantMap ret; + QVariantList timeEventItems; + for (int i = 0; i < timeDescriptor->timeEventItems()->rowCount(); i++) { + timeEventItems.append(packTimeEventItem(timeDescriptor->timeEventItems()->get(i))); + } + if (!timeEventItems.isEmpty()) { + ret.insert("timeEventItems", timeEventItems); + } + QVariantList calendarItems; + for (int i = 0; i < timeDescriptor->calendarItems()->rowCount(); i++) { + calendarItems.append(packCalendarItem(timeDescriptor->calendarItems()->get(i))); + } + if (!calendarItems.isEmpty()) { + ret.insert("calendarItems", calendarItems); + } + return ret; +} + +QVariantMap RuleManager::packTimeEventItem(TimeEventItem *timeEventItem) +{ + QVariantMap ret; + if (!timeEventItem->time().isNull()) { + ret.insert("time", timeEventItem->time().toString("hh:mm")); + } + if (!timeEventItem->dateTime().isNull()) { + ret.insert("datetime", timeEventItem->dateTime().toSecsSinceEpoch()); + } + ret.insert("repeating", packRepeatingOption(timeEventItem->repeatingOption())); + return ret; +} + +QVariantMap RuleManager::packCalendarItem(CalendarItem *calendarItem) +{ + QVariantMap ret; + ret.insert("duration", calendarItem->duration()); + if (!calendarItem->dateTime().isNull()) { + ret.insert("datetime", calendarItem->dateTime().toSecsSinceEpoch()); + } + if (!calendarItem->startTime().isNull()) { + ret.insert("startTime", calendarItem->startTime().toString("hh:mm")); + } + ret.insert("repeating", packRepeatingOption(calendarItem->repeatingOption())); + return ret; +} + +QVariantMap RuleManager::packRepeatingOption(RepeatingOption *repeatingOption) +{ + QVariantMap ret; + QMetaEnum repeatingModeEnum = QMetaEnum::fromType(); + ret.insert("mode", repeatingModeEnum.valueToKey(repeatingOption->repeatingMode())); + if (!repeatingOption->weekDays().isEmpty()) { + ret.insert("weekDays", repeatingOption->weekDays()); + } + if (!repeatingOption->monthDays().isEmpty()) { + ret.insert("monthDays", repeatingOption->monthDays()); + } + return ret; +} + +QVariantList RuleManager::packRuleActions(RuleActions *ruleActions) +{ + QVariantList ret; + for (int i = 0; i < ruleActions->rowCount(); i++) { + QVariantMap ruleAction; + RuleAction *ra = ruleActions->get(i); + if (!ra->actionTypeId().isNull() && !ra->deviceId().isNull()) { + ruleAction.insert("deviceId", ra->deviceId()); + ruleAction.insert("actionTypeId", ra->actionTypeId()); + } else if (!ra->deviceId().isNull() && !ra->browserItemId().isEmpty()) { + ruleAction.insert("deviceId", ra->deviceId()); + ruleAction.insert("browserItemId", ra->browserItemId()); + } else { + ruleAction.insert("interface", ra->interfaceName()); + ruleAction.insert("interfaceAction", ra->interfaceAction()); + } + if (ra->ruleActionParams()->rowCount() > 0) { + QVariantList ruleActionParams; + for (int j = 0; j < ra->ruleActionParams()->rowCount(); j++) { + QVariantMap ruleActionParam; + RuleActionParam *rap = ruleActions->get(i)->ruleActionParams()->get(j); + if (!rap->paramTypeId().isNull()) { + ruleActionParam.insert("paramTypeId", rap->paramTypeId()); + } else { + ruleActionParam.insert("paramName", rap->paramName()); + } + if (rap->isValueBased()) { + ruleActionParam.insert("value", rap->value()); + } else if (rap->isEventParamBased()) { + ruleActionParam.insert("eventTypeId", rap->eventTypeId()); + ruleActionParam.insert("eventParamTypeId", rap->eventParamTypeId()); + } else { + ruleActionParam.insert("stateDeviceId", rap->stateDeviceId()); + ruleActionParam.insert("stateTypeId", rap->stateTypeId()); + } + ruleActionParams.append(ruleActionParam); + } + ruleAction.insert("ruleActionParams", ruleActionParams); + } + ret.append(ruleAction); + } + + return ret; +} + +QVariantMap RuleManager::packStateEvaluator(StateEvaluator *stateEvaluator) +{ + QVariantMap ret; + QMetaEnum stateOperatorEnum = QMetaEnum::fromType(); + ret.insert("operator", stateOperatorEnum.valueToKey(stateEvaluator->stateOperator())); + QVariantMap stateDescriptor; + if (!stateEvaluator->stateDescriptor()->deviceId().isNull() && !stateEvaluator->stateDescriptor()->stateTypeId().isNull()) { + stateDescriptor.insert("deviceId", stateEvaluator->stateDescriptor()->deviceId()); + stateDescriptor.insert("stateTypeId", stateEvaluator->stateDescriptor()->stateTypeId()); + } else { + stateDescriptor.insert("interface", stateEvaluator->stateDescriptor()->interfaceName()); + stateDescriptor.insert("interfaceState", stateEvaluator->stateDescriptor()->interfaceState()); + } + QMetaEnum valueOperatorEnum = QMetaEnum::fromType(); + stateDescriptor.insert("operator", valueOperatorEnum.valueToKeys(stateEvaluator->stateDescriptor()->valueOperator())); + stateDescriptor.insert("value", stateEvaluator->stateDescriptor()->value()); + ret.insert("stateDescriptor", stateDescriptor); + QVariantList childEvaluators; + for (int i = 0; i < stateEvaluator->childEvaluators()->rowCount(); i++) { + childEvaluators.append(packStateEvaluator(stateEvaluator->childEvaluators()->get(i))); + } + ret.insert("childEvaluators", childEvaluators); + return ret; } diff --git a/libnymea-app/rulemanager.h b/libnymea-app/rulemanager.h index 82dd099a..2df2f602 100644 --- a/libnymea-app/rulemanager.h +++ b/libnymea-app/rulemanager.h @@ -37,8 +37,14 @@ #include "jsonrpc/jsonhandler.h" class JsonRpcClient; +class EventDescriptors; +class TimeDescriptor; +class TimeEventItem; +class CalendarItem; +class RepeatingOption; class StateEvaluator; class RuleAction; +class RuleActions; class RuleManager : public JsonHandler { @@ -57,8 +63,8 @@ public: Q_INVOKABLE Rule* createNewRule(); - Q_INVOKABLE void addRule(const QVariantMap params); - Q_INVOKABLE void addRule(Rule *rule); + Q_INVOKABLE int addRule(const QVariantMap params); + Q_INVOKABLE int addRule(Rule *rule); Q_INVOKABLE void removeRule(const QUuid &ruleId); Q_INVOKABLE void editRule(Rule *rule); Q_INVOKABLE void executeActions(const QString &ruleId); @@ -81,8 +87,17 @@ private: RuleAction* parseRuleAction(const QVariantMap &ruleAction); void parseTimeDescriptor(const QVariantMap &timeDescriptor, Rule *rule); + QVariantMap packRule(Rule *rule); + QVariantList packEventDescriptors(EventDescriptors *eventDescriptors); + QVariantMap packTimeDescriptor(TimeDescriptor *timeDescriptor); + QVariantMap packTimeEventItem(TimeEventItem *timeEventItem); + QVariantMap packCalendarItem(CalendarItem *calendarItem); + QVariantMap packRepeatingOption(RepeatingOption *repeatingOption); + QVariantList packRuleActions(RuleActions *ruleActions); + QVariantMap packStateEvaluator(StateEvaluator *stateEvaluator); + signals: - void addRuleReply(const QString &ruleError, const QString &ruleId); + void addRuleReply(int commandId, const QString &ruleError, const QString &ruleId); void editRuleReply(const QString &ruleError); private: diff --git a/libnymea-app/ruletemplates/calendaritemtemplate.cpp b/libnymea-app/ruletemplates/calendaritemtemplate.cpp new file mode 100644 index 00000000..d6bb8f80 --- /dev/null +++ b/libnymea-app/ruletemplates/calendaritemtemplate.cpp @@ -0,0 +1,50 @@ +#include "calendaritemtemplate.h" + + +CalendarItemTemplate::CalendarItemTemplate(int duration, const QDateTime &dateTime, const QTime &startTime, RepeatingOption *repeatingOption, bool editable, QObject *parent): + QObject(parent), + m_duration(duration), + m_dateTime(dateTime), + m_startTime(startTime), + m_repeatingOption(repeatingOption), + m_editable(editable) +{ + m_repeatingOption->setParent(this); +} + +int CalendarItemTemplate::duration() const +{ + return m_duration; +} + +QDateTime CalendarItemTemplate::dateTime() const +{ + return m_dateTime; +} + +QTime CalendarItemTemplate::startTime() const +{ + return m_startTime; +} + +RepeatingOption *CalendarItemTemplate::repeatingOption() +{ + return m_repeatingOption; +} + +bool CalendarItemTemplate::editable() const +{ + return m_editable; +} + +CalendarItem *CalendarItemTemplate::createCalendarItem() const +{ + CalendarItem *ret = new CalendarItem(); + ret->setDateTime(m_dateTime); + ret->setDuration(m_duration); + ret->setStartTime(m_startTime); + ret->repeatingOption()->setWeekDays(m_repeatingOption->weekDays()); + ret->repeatingOption()->setMonthDays(m_repeatingOption->monthDays()); + ret->repeatingOption()->setRepeatingMode(m_repeatingOption->repeatingMode()); + return ret; +} diff --git a/libnymea-app/ruletemplates/calendaritemtemplate.h b/libnymea-app/ruletemplates/calendaritemtemplate.h new file mode 100644 index 00000000..f2f28764 --- /dev/null +++ b/libnymea-app/ruletemplates/calendaritemtemplate.h @@ -0,0 +1,65 @@ +#ifndef CALENDARITEMTEMPLATE_H +#define CALENDARITEMTEMPLATE_H + +#include "types/repeatingoption.h" +#include "types/calendaritem.h" + +#include +#include +#include + +class CalendarItemTemplate : public QObject +{ + Q_OBJECT + Q_PROPERTY(int duration READ duration CONSTANT) + Q_PROPERTY(QDateTime dateTime READ dateTime CONSTANT) + Q_PROPERTY(QTime startTime READ startTime CONSTANT) + Q_PROPERTY(RepeatingOption* repeatingOption READ repeatingOption CONSTANT) + Q_PROPERTY(bool editable READ editable CONSTANT) +public: + explicit CalendarItemTemplate(int duration, const QDateTime &dateTime, const QTime &startTime, RepeatingOption *repeatingOption, bool editable, QObject *parent = nullptr); + + int duration() const; + QDateTime dateTime() const; + QTime startTime() const; + RepeatingOption* repeatingOption(); + bool editable() const; + + Q_INVOKABLE CalendarItem *createCalendarItem() const; + +private: + int m_duration = 0; + QDateTime m_dateTime; + QTime m_startTime; + RepeatingOption *m_repeatingOption = nullptr; + bool m_editable = true; +}; + +class CalendarItemTemplates: public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(int count READ rowCount CONSTANT) + +public: + CalendarItemTemplates(QObject *parent = nullptr): QAbstractListModel(parent) {} + int rowCount(const QModelIndex &parent = QModelIndex()) const override { Q_UNUSED(parent); return m_list.count(); } + QVariant data(const QModelIndex &index, int role) const override { Q_UNUSED(index); Q_UNUSED(role); return QVariant(); } + + Q_INVOKABLE CalendarItemTemplate* get(int index) const { + if (index < 0 || index >= m_list.count()) { + return nullptr; + } + return m_list.at(index); + } + + void addCalendarItemTemplate(CalendarItemTemplate *calendarItemTemplate) { + calendarItemTemplate->setParent(this); + beginInsertRows(QModelIndex(), m_list.count(), m_list.count()); + m_list.append(calendarItemTemplate); + endInsertRows(); + } +private: + QList m_list; +}; + +#endif // CALENDARITEMTEMPLATE_H diff --git a/libnymea-app/ruletemplates/ruletemplate.cpp b/libnymea-app/ruletemplates/ruletemplate.cpp index 13725f59..3f66711c 100644 --- a/libnymea-app/ruletemplates/ruletemplate.cpp +++ b/libnymea-app/ruletemplates/ruletemplate.cpp @@ -30,6 +30,7 @@ #include "ruletemplate.h" #include "eventdescriptortemplate.h" +#include "timedescriptortemplate.h" #include "stateevaluatortemplate.h" #include "ruleactiontemplate.h" @@ -85,6 +86,20 @@ void RuleTemplate::setStateEvaluatorTemplate(StateEvaluatorTemplate *stateEvalua m_stateEvaluatorTemplate = stateEvaluatorTemplate; } +TimeDescriptorTemplate *RuleTemplate::timeDescriptorTemplate() const +{ + return m_timeDescriptorTemplate; +} + +void RuleTemplate::setTimeDescriptorTemplate(TimeDescriptorTemplate *timeDescriptorTemplate) +{ + if (m_timeDescriptorTemplate) { + m_timeDescriptorTemplate->deleteLater(); + } + timeDescriptorTemplate->setParent(this); + m_timeDescriptorTemplate = timeDescriptorTemplate; +} + RuleActionTemplates *RuleTemplate::ruleActionTemplates() const { return m_ruleActionTemplates; diff --git a/libnymea-app/ruletemplates/ruletemplate.h b/libnymea-app/ruletemplates/ruletemplate.h index 57a815e0..73c50a14 100644 --- a/libnymea-app/ruletemplates/ruletemplate.h +++ b/libnymea-app/ruletemplates/ruletemplate.h @@ -36,6 +36,7 @@ class EventDescriptorTemplates; class RuleActionTemplates; class StateEvaluatorTemplate; +class TimeDescriptorTemplate; class RuleTemplate : public QObject { @@ -44,6 +45,7 @@ class RuleTemplate : public QObject Q_PROPERTY(QString ruleNameTemplate READ ruleNameTemplate CONSTANT) Q_PROPERTY(QStringList interfaces READ interfaces CONSTANT) Q_PROPERTY(EventDescriptorTemplates* eventDescriptorTemplates READ eventDescriptorTemplates CONSTANT) + Q_PROPERTY(TimeDescriptorTemplate* timeDescriptorTemplate READ timeDescriptorTemplate CONSTANT) Q_PROPERTY(StateEvaluatorTemplate* stateEvaluatorTemplate READ stateEvaluatorTemplate CONSTANT) Q_PROPERTY(RuleActionTemplates* ruleActionTemplates READ ruleActionTemplates CONSTANT) Q_PROPERTY(RuleActionTemplates* ruleExitActionTemplates READ ruleExitActionTemplates CONSTANT) @@ -58,6 +60,8 @@ public: EventDescriptorTemplates* eventDescriptorTemplates() const; StateEvaluatorTemplate* stateEvaluatorTemplate() const; void setStateEvaluatorTemplate(StateEvaluatorTemplate *stateEvaluatorTemplate); + TimeDescriptorTemplate* timeDescriptorTemplate() const; + void setTimeDescriptorTemplate(TimeDescriptorTemplate *timeDescriptorTemplate); RuleActionTemplates* ruleActionTemplates() const; RuleActionTemplates* ruleExitActionTemplates() const; @@ -67,6 +71,7 @@ private: QString m_ruleNameTemplate; EventDescriptorTemplates* m_eventDescriptorTemplates = nullptr; StateEvaluatorTemplate* m_stateEvaluatorTemplate = nullptr; + TimeDescriptorTemplate* m_timeDescriptorTemplate = nullptr; RuleActionTemplates *m_ruleActionTemplates = nullptr; RuleActionTemplates *m_ruleExitActionTemplates = nullptr; }; diff --git a/libnymea-app/ruletemplates/ruletemplates.cpp b/libnymea-app/ruletemplates/ruletemplates.cpp index e81bd934..05e7fcbb 100644 --- a/libnymea-app/ruletemplates/ruletemplates.cpp +++ b/libnymea-app/ruletemplates/ruletemplates.cpp @@ -32,12 +32,16 @@ #include "ruletemplate.h" #include "eventdescriptortemplate.h" +#include "timedescriptortemplate.h" +#include "calendaritemtemplate.h" +#include "timeeventitemtemplate.h" #include "ruleactiontemplate.h" #include "stateevaluatortemplate.h" #include "ruleactionparamtemplate.h" #include "types/ruleactionparam.h" #include "types/ruleactionparams.h" +#include "types/repeatingoption.h" #include "devicesproxy.h" #include @@ -114,6 +118,12 @@ RuleTemplates::RuleTemplates(QObject *parent) : QAbstractListModel(parent) t->setStateEvaluatorTemplate(loadStateEvaluatorTemplate(ruleTemplate.value("stateEvaluatorTemplate").toMap())); } + // TimeDescriptorTemplate + if (ruleTemplate.contains("timeDescriptorTemplate")) { + + t->setTimeDescriptorTemplate(loadTimeDescriptorTemplate(ruleTemplate.value("timeDescriptorTemplate").toMap())); + } + // RuleActionTemplates foreach (const QVariant &ruleActionVariant, ruleTemplate.value("ruleActionTemplates").toList()) { QVariantMap ruleActionTemplate = ruleActionVariant.toMap(); @@ -240,6 +250,42 @@ StateEvaluatorTemplate *RuleTemplates::loadStateEvaluatorTemplate(const QVariant return set; } +TimeDescriptorTemplate *RuleTemplates::loadTimeDescriptorTemplate(const QVariantMap &timeDescriptorTemplate) const +{ + TimeDescriptorTemplate *tdt = new TimeDescriptorTemplate(); + foreach (const QVariant &childVariant, timeDescriptorTemplate.value("calendarItemTemplates").toList()) { + QVariantMap childMap = childVariant.toMap(); + + int duration = childMap.value("duration").toInt(); + QDateTime dateTime = childMap.value("dateTime").toDateTime(); + QTime startTime = childMap.value("startTime").toTime(); + bool editable = childMap.value("editable", true).toBool(); + RepeatingOption *repeatingOption = loadRepeatingOption(childMap.value("repeatingOption").toMap()); + CalendarItemTemplate *cit = new CalendarItemTemplate(duration, dateTime, startTime, repeatingOption, editable, tdt); + tdt->calendarItemTemplates()->addCalendarItemTemplate(cit); + } + foreach (const QVariant &childVariant, timeDescriptorTemplate.value("timeEventItemTemplates").toList()) { + QVariantMap childMap = childVariant.toMap(); + QDateTime dateTime = childMap.value("dateTime").toDateTime(); + QTime time = childMap.value("time").toTime(); + bool editable = childMap.value("editable", true).toBool(); + RepeatingOption *repeatingOption = loadRepeatingOption(childMap.value("repeatingOption").toMap()); + TimeEventItemTemplate *teit = new TimeEventItemTemplate(dateTime, time, repeatingOption, editable, tdt); + tdt->timeEventItemTemplates()->addTimeEventItemTemplate(teit); + } + return tdt; +} + +RepeatingOption *RuleTemplates::loadRepeatingOption(const QVariantMap &repeatingOptionMap) const +{ + RepeatingOption *repeatingOption = new RepeatingOption(); + repeatingOption->setWeekDays(repeatingOptionMap.value("weekDays").toList()); + repeatingOption->setMonthDays(repeatingOptionMap.value("monthDays").toList()); + QMetaEnum repeatingModeEnum = QMetaEnum::fromType(); + repeatingOption->setRepeatingMode(static_cast(repeatingModeEnum.keyToValue(repeatingOptionMap.value("repeatingMode").toString().toUtf8().data()))); + return repeatingOption; +} + bool RuleTemplatesFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { Q_UNUSED(source_parent) diff --git a/libnymea-app/ruletemplates/ruletemplates.h b/libnymea-app/ruletemplates/ruletemplates.h index b15f063b..20c4fd19 100644 --- a/libnymea-app/ruletemplates/ruletemplates.h +++ b/libnymea-app/ruletemplates/ruletemplates.h @@ -35,6 +35,8 @@ class RuleTemplate; class StateEvaluatorTemplate; +class TimeDescriptorTemplate; +class RepeatingOption; class DevicesProxy; class RuleTemplates : public QAbstractListModel @@ -60,6 +62,8 @@ signals: private: StateEvaluatorTemplate* loadStateEvaluatorTemplate(const QVariantMap &stateEvaluatorTemplate) const; + TimeDescriptorTemplate* loadTimeDescriptorTemplate(const QVariantMap &timeDescriptorTemplate) const; + RepeatingOption* loadRepeatingOption(const QVariantMap &repeatingOptionMap) const; private: QList m_list; diff --git a/libnymea-app/ruletemplates/timedescriptortemplate.cpp b/libnymea-app/ruletemplates/timedescriptortemplate.cpp new file mode 100644 index 00000000..60f8b311 --- /dev/null +++ b/libnymea-app/ruletemplates/timedescriptortemplate.cpp @@ -0,0 +1,21 @@ +#include "timedescriptortemplate.h" + +#include "calendaritemtemplate.h" +#include "timeeventitemtemplate.h" + +TimeDescriptorTemplate::TimeDescriptorTemplate(QObject *parent): + QObject(parent) +{ + m_calendarItemTemplates = new CalendarItemTemplates(this); + m_timeEventItemTemplates = new TimeEventItemTemplates(this); +} + +CalendarItemTemplates *TimeDescriptorTemplate::calendarItemTemplates() const +{ + return m_calendarItemTemplates; +} + +TimeEventItemTemplates *TimeDescriptorTemplate::timeEventItemTemplates() const +{ + return m_timeEventItemTemplates; +} diff --git a/libnymea-app/ruletemplates/timedescriptortemplate.h b/libnymea-app/ruletemplates/timedescriptortemplate.h new file mode 100644 index 00000000..2b0498ef --- /dev/null +++ b/libnymea-app/ruletemplates/timedescriptortemplate.h @@ -0,0 +1,26 @@ +#ifndef TIMEDESCRIPTORTEMPLATE_H +#define TIMEDESCRIPTORTEMPLATE_H + +#include + +class CalendarItemTemplates; +class TimeEventItemTemplates; + +class TimeDescriptorTemplate : public QObject +{ + Q_OBJECT + Q_PROPERTY(CalendarItemTemplates* calendarItemTemplates READ calendarItemTemplates CONSTANT) + Q_PROPERTY(TimeEventItemTemplates* timeEventItemTemplates READ timeEventItemTemplates CONSTANT) + +public: + explicit TimeDescriptorTemplate(QObject *parent = nullptr); + + CalendarItemTemplates* calendarItemTemplates() const; + TimeEventItemTemplates* timeEventItemTemplates() const; + +private: + CalendarItemTemplates *m_calendarItemTemplates = nullptr; + TimeEventItemTemplates *m_timeEventItemTemplates = nullptr; +}; + +#endif // TIMEDESCRIPTORTEMPLATE_H diff --git a/libnymea-app/ruletemplates/timeeventitemtemplate.cpp b/libnymea-app/ruletemplates/timeeventitemtemplate.cpp new file mode 100644 index 00000000..b7cd976b --- /dev/null +++ b/libnymea-app/ruletemplates/timeeventitemtemplate.cpp @@ -0,0 +1,42 @@ +#include "timeeventitemtemplate.h" + +TimeEventItemTemplate::TimeEventItemTemplate(const QDateTime &dateTime, const QTime &time, RepeatingOption *repeatingOption, bool editable, QObject *parent): + QObject(parent), + m_dateTime(dateTime), + m_time(time), + m_repeatingOption(repeatingOption), + m_editable(editable) +{ + m_repeatingOption->setParent(this); +} + +QDateTime TimeEventItemTemplate::dateTime() const +{ + return m_dateTime; +} + +QTime TimeEventItemTemplate::time() const +{ + return m_time; +} + +RepeatingOption *TimeEventItemTemplate::repeatingOption() const +{ + return m_repeatingOption; +} + +bool TimeEventItemTemplate::editable() const +{ + return m_editable; +} + +TimeEventItem *TimeEventItemTemplate::createTimeEventItem() const +{ + TimeEventItem *item = new TimeEventItem(); + item->setDateTime(m_dateTime); + item->setTime(m_time); + item->repeatingOption()->setWeekDays(m_repeatingOption->weekDays()); + item->repeatingOption()->setMonthDays(m_repeatingOption->monthDays()); + item->repeatingOption()->setRepeatingMode(m_repeatingOption->repeatingMode()); + return item; +} diff --git a/libnymea-app/ruletemplates/timeeventitemtemplate.h b/libnymea-app/ruletemplates/timeeventitemtemplate.h new file mode 100644 index 00000000..afd8f110 --- /dev/null +++ b/libnymea-app/ruletemplates/timeeventitemtemplate.h @@ -0,0 +1,63 @@ +#ifndef TIMEEVENTITEMTEMPLATE_H +#define TIMEEVENTITEMTEMPLATE_H + +#include +#include +#include + +#include "types/repeatingoption.h" +#include "types/timeeventitem.h" + +class TimeEventItemTemplate : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDateTime dateTime READ dateTime CONSTANT) + Q_PROPERTY(QTime time READ time CONSTANT) + Q_PROPERTY(RepeatingOption* repeatingOption READ repeatingOption CONSTANT) + Q_PROPERTY(bool editable READ editable CONSTANT) + +public: + explicit TimeEventItemTemplate(const QDateTime &dateTime, const QTime &time, RepeatingOption *repeatingOption, bool editable, QObject *parent = nullptr); + + QDateTime dateTime() const; + QTime time() const; + RepeatingOption* repeatingOption() const; + bool editable() const; + + Q_INVOKABLE TimeEventItem* createTimeEventItem() const; + +private: + QDateTime m_dateTime; + QTime m_time; + RepeatingOption* m_repeatingOption = nullptr; + bool m_editable = true; +}; + +class TimeEventItemTemplates: public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(int count READ rowCount CONSTANT) + +public: + TimeEventItemTemplates(QObject *parent = nullptr): QAbstractListModel(parent) {} + int rowCount(const QModelIndex &parent = QModelIndex()) const override { Q_UNUSED(parent) return m_list.count(); } + QVariant data(const QModelIndex &index, int role) const override { Q_UNUSED(index) Q_UNUSED(role) return QVariant(); } + + Q_INVOKABLE TimeEventItemTemplate* get(int index) const { + if (index < 0 || index >= m_list.count()) { + return nullptr; + } + return m_list.at(index); + } + + void addTimeEventItemTemplate(TimeEventItemTemplate *timeEventItemTemplate) { + timeEventItemTemplate->setParent(this); + beginInsertRows(QModelIndex(), m_list.count(), m_list.count()); + m_list.append(timeEventItemTemplate); + endInsertRows(); + } +private: + QList m_list; +}; + +#endif // TIMEEVENTITEMTEMPLATE_H diff --git a/libnymea-app/tagsmanager.cpp b/libnymea-app/tagsmanager.cpp index df01ad34..506df48d 100644 --- a/libnymea-app/tagsmanager.cpp +++ b/libnymea-app/tagsmanager.cpp @@ -133,21 +133,31 @@ void TagsManager::handleTagsNotification(const QVariantMap ¶ms) } else if (notification == "Tags.TagRemoved") { for (int i = 0; i < m_tags->rowCount(); i++) { Tag* tag = m_tags->get(i); - if (tagMap.value("deviceId").toUuid() == tag->deviceId() && - tagMap.value("ruleId").toUuid() == tag->ruleId() && - tagMap.value("tagId").toString() == tag->tagId()) { + QUuid thingId; + if (m_jsonClient->ensureServerVersion("5.0")) { + thingId = tagMap.value("thingId").toUuid(); + } else { + thingId = tagMap.value("deviceId").toUuid(); + } + QUuid ruleId = tagMap.value("ruleId").toUuid(); + QString tagId = tagMap.value("tagId").toString(); + if (thingId == tag->thingId() && ruleId == tag->ruleId() && tagId == tag->tagId()) { m_tags->removeTag(tag); return; } } } else if (notification == "Tags.TagValueChanged") { - qDebug() << "tag value changed"; for (int i = 0; i < m_tags->rowCount(); i++) { Tag* tag = m_tags->get(i); - if (tagMap.value("deviceId").toUuid() == tag->deviceId() && - tagMap.value("ruleId").toUuid() == tag->ruleId() && - tagMap.value("tagId").toString() == tag->tagId()) { - qDebug() << "Found tag"; + QUuid thingId; + if (m_jsonClient->ensureServerVersion("5.0")) { + thingId = tagMap.value("thingId").toUuid(); + } else { + thingId = tagMap.value("deviceId").toUuid(); + } + QUuid ruleId = tagMap.value("ruleId").toUuid(); + QString tagId = tagMap.value("tagId").toString(); + if (thingId == tag->thingId() && ruleId == tag->ruleId() && tagId == tag->tagId()) { tag->setValue(tagMap.value("value").toString()); } } @@ -181,14 +191,19 @@ void TagsManager::removeTagReply(const QVariantMap ¶ms) Tag* TagsManager::unpackTag(const QVariantMap &tagMap) { - QString deviceId = tagMap.value("deviceId").toString(); + QString thingId; + if (m_jsonClient->ensureServerVersion("5.0")) { + thingId = tagMap.value("thingId").toString(); + } else { + thingId = tagMap.value("deviceId").toString(); + } QString ruleId = tagMap.value("ruleId").toString(); QString tagId = tagMap.value("tagId").toString(); QString value = tagMap.value("value").toString(); Tag *tag = nullptr; - if (!deviceId.isEmpty()) { + if (!thingId.isEmpty()) { tag = new Tag(tagId, value); - tag->setDeviceId(deviceId); + tag->setThingId(thingId); } else if (!ruleId.isEmpty()) { tag = new Tag(tagId, value); tag->setRuleId(ruleId); diff --git a/libnymea-app/types/eventdescriptor.cpp b/libnymea-app/types/eventdescriptor.cpp index 6b550b91..de991393 100644 --- a/libnymea-app/types/eventdescriptor.cpp +++ b/libnymea-app/types/eventdescriptor.cpp @@ -36,25 +36,25 @@ EventDescriptor::EventDescriptor(QObject *parent) : QObject(parent) m_paramDescriptors = new ParamDescriptors(this); } -QString EventDescriptor::deviceId() const +QUuid EventDescriptor::thingId() const { - return m_deviceId; + return m_thingId; } -void EventDescriptor::setDeviceId(const QString &deviceId) +void EventDescriptor::setThingId(const QUuid &thingId) { - if (m_deviceId != deviceId) { - m_deviceId = deviceId; - emit deviceIdChanged(); + if (m_thingId != thingId) { + m_thingId = thingId; + emit thingIdChanged(); } } -QString EventDescriptor::eventTypeId() const +QUuid EventDescriptor::eventTypeId() const { return m_eventTypeId; } -void EventDescriptor::setEventTypeId(const QString &eventTypeId) +void EventDescriptor::setEventTypeId(const QUuid &eventTypeId) { if (m_eventTypeId != eventTypeId) { m_eventTypeId = eventTypeId; @@ -96,7 +96,7 @@ ParamDescriptors *EventDescriptor::paramDescriptors() const EventDescriptor *EventDescriptor::clone() const { EventDescriptor *ret = new EventDescriptor(); - ret->setDeviceId(this->deviceId()); + ret->setThingId(this->thingId()); ret->setEventTypeId(this->eventTypeId()); ret->setInterfaceName(this->interfaceName()); ret->setInterfaceEvent(this->interfaceEvent()); @@ -110,7 +110,7 @@ EventDescriptor *EventDescriptor::clone() const #define COMPARE_PTR(a, b) if (!a->operator==(b)) { qDebug() << a << "!=" << b; return false; } bool EventDescriptor::operator==(EventDescriptor *other) const { - COMPARE(m_deviceId, other->deviceId()); + COMPARE(m_thingId, other->thingId()); COMPARE(m_eventTypeId, other->eventTypeId()); COMPARE(m_interfaceName, other->interfaceName()); COMPARE(m_interfaceEvent, other->interfaceEvent()); diff --git a/libnymea-app/types/eventdescriptor.h b/libnymea-app/types/eventdescriptor.h index 8d7ddbd2..ad893144 100644 --- a/libnymea-app/types/eventdescriptor.h +++ b/libnymea-app/types/eventdescriptor.h @@ -39,8 +39,9 @@ class EventDescriptor : public QObject { Q_OBJECT - Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) - Q_PROPERTY(QString eventTypeId READ eventTypeId WRITE setEventTypeId NOTIFY eventTypeIdChanged) + Q_PROPERTY(QUuid thingId READ thingId WRITE setThingId NOTIFY thingIdChanged) + Q_PROPERTY(QUuid deviceId READ thingId WRITE setThingId NOTIFY thingIdChanged) + Q_PROPERTY(QUuid eventTypeId READ eventTypeId WRITE setEventTypeId NOTIFY eventTypeIdChanged) Q_PROPERTY(QString interfaceName READ interfaceName WRITE setInterfaceName NOTIFY interfaceNameChanged) Q_PROPERTY(QString interfaceEvent READ interfaceEvent WRITE setInterfaceEvent NOTIFY interfaceEventChanged) @@ -50,11 +51,11 @@ class EventDescriptor : public QObject public: explicit EventDescriptor(QObject *parent = nullptr); - QString deviceId() const; - void setDeviceId(const QString &deviceId); + QUuid thingId() const; + void setThingId(const QUuid &thingId); - QString eventTypeId() const; - void setEventTypeId(const QString &eventTypeId); + QUuid eventTypeId() const; + void setEventTypeId(const QUuid &eventTypeId); QString interfaceName() const; void setInterfaceName(const QString &interfaceName); @@ -68,14 +69,14 @@ public: bool operator==(EventDescriptor* other) const; signals: - void deviceIdChanged(); + void thingIdChanged(); void eventTypeIdChanged(); void interfaceNameChanged(); void interfaceEventChanged(); private: - QString m_deviceId; - QString m_eventTypeId; + QUuid m_thingId; + QUuid m_eventTypeId; QString m_interfaceName; QString m_interfaceEvent; diff --git a/libnymea-app/types/eventdescriptors.cpp b/libnymea-app/types/eventdescriptors.cpp index 8bc4e577..75c59eb3 100644 --- a/libnymea-app/types/eventdescriptors.cpp +++ b/libnymea-app/types/eventdescriptors.cpp @@ -48,8 +48,9 @@ int EventDescriptors::rowCount(const QModelIndex &parent) const QVariant EventDescriptors::data(const QModelIndex &index, int role) const { switch (role) { + case RoleThingId: case RoleDeviceId: - return m_list.at(index.row())->deviceId(); + return m_list.at(index.row())->thingId(); case RoleEventTypeId: return m_list.at(index.row())->eventTypeId(); } @@ -59,6 +60,7 @@ QVariant EventDescriptors::data(const QModelIndex &index, int role) const QHash EventDescriptors::roleNames() const { QHash roles; + roles.insert(RoleThingId, "thingId"); roles.insert(RoleDeviceId, "deviceId"); roles.insert(RoleEventTypeId, "eventId"); return roles; diff --git a/libnymea-app/types/eventdescriptors.h b/libnymea-app/types/eventdescriptors.h index 6088a085..89b542dd 100644 --- a/libnymea-app/types/eventdescriptors.h +++ b/libnymea-app/types/eventdescriptors.h @@ -41,6 +41,7 @@ class EventDescriptors : public QAbstractListModel Q_PROPERTY(int count READ rowCount NOTIFY countChanged) public: enum Roles { + RoleThingId, RoleDeviceId, RoleEventTypeId }; diff --git a/libnymea-app/types/logentry.cpp b/libnymea-app/types/logentry.cpp index be6daefb..896472ad 100644 --- a/libnymea-app/types/logentry.cpp +++ b/libnymea-app/types/logentry.cpp @@ -32,11 +32,11 @@ #include -LogEntry::LogEntry(const QDateTime ×tamp, const QVariant &value, const QUuid &deviceId, const QUuid &typeId, LoggingSource source, LoggingEventType loggingEventType, QObject *parent): +LogEntry::LogEntry(const QDateTime ×tamp, const QVariant &value, const QUuid &thingId, const QUuid &typeId, LoggingSource source, LoggingEventType loggingEventType, QObject *parent): QObject(parent), m_value(value), m_timeStamp(timestamp), - m_deviceId(deviceId), + m_thingId(thingId), m_typeId(typeId), m_source(source), m_loggingEventType(loggingEventType) @@ -54,9 +54,9 @@ QDateTime LogEntry::timestamp() const return m_timeStamp; } -QUuid LogEntry::deviceId() const +QUuid LogEntry::thingId() const { - return m_deviceId; + return m_thingId; } QUuid LogEntry::typeId() const diff --git a/libnymea-app/types/logentry.h b/libnymea-app/types/logentry.h index 62222aef..4977755c 100644 --- a/libnymea-app/types/logentry.h +++ b/libnymea-app/types/logentry.h @@ -40,7 +40,8 @@ class LogEntry : public QObject { Q_OBJECT Q_PROPERTY(QVariant value READ value CONSTANT) - Q_PROPERTY(QUuid deviceId READ deviceId CONSTANT) + Q_PROPERTY(QUuid thingId READ thingId CONSTANT) + Q_PROPERTY(QUuid deviceId READ thingId CONSTANT) Q_PROPERTY(QUuid typeId READ typeId CONSTANT) Q_PROPERTY(LoggingSource source READ source CONSTANT) Q_PROPERTY(LoggingEventType loggingEventType READ loggingEventType CONSTANT) @@ -70,11 +71,11 @@ public: }; Q_ENUM(LoggingEventType) - explicit LogEntry(const QDateTime ×tamp, const QVariant &value, const QUuid &deviceId = QUuid(), const QUuid &typeId = QUuid(), LoggingSource source = LoggingSourceSystem, LoggingEventType loggingEventType = LoggingEventTypeTrigger, QObject *parent = nullptr); + explicit LogEntry(const QDateTime ×tamp, const QVariant &value, const QUuid &thingId = QUuid(), const QUuid &typeId = QUuid(), LoggingSource source = LoggingSourceSystem, LoggingEventType loggingEventType = LoggingEventTypeTrigger, QObject *parent = nullptr); QVariant value() const; QDateTime timestamp() const; - QUuid deviceId() const; + QUuid thingId() const; QUuid typeId() const; LoggingSource source() const; LoggingEventType loggingEventType() const; @@ -86,7 +87,7 @@ public: private: QVariant m_value; QDateTime m_timeStamp; - QUuid m_deviceId; + QUuid m_thingId; QUuid m_typeId; LoggingSource m_source; LoggingEventType m_loggingEventType; diff --git a/libnymea-app/types/rule.cpp b/libnymea-app/types/rule.cpp index eb17e88e..3276e095 100644 --- a/libnymea-app/types/rule.cpp +++ b/libnymea-app/types/rule.cpp @@ -220,8 +220,8 @@ QDebug operator <<(QDebug &dbg, Rule *rule) for (int i = 0; i < rule->eventDescriptors()->rowCount(); i++) { EventDescriptor *ed = rule->eventDescriptors()->get(i); dbg << " " << i << ":"; - if (!ed->deviceId().isNull() && !ed->eventTypeId().isNull()) { - dbg << "Device ID:" << ed->deviceId() << "Event Type ID:" << ed->eventTypeId() << endl; + if (!ed->thingId().isNull() && !ed->eventTypeId().isNull()) { + dbg << "Thing ID:" << ed->thingId() << "Event Type ID:" << ed->eventTypeId() << endl; } else { dbg << "Interface Name:" << ed->interfaceName() << "Event Name:" << ed->interfaceEvent() << endl; } diff --git a/libnymea-app/types/ruleaction.h b/libnymea-app/types/ruleaction.h index 71b32d43..e154f257 100644 --- a/libnymea-app/types/ruleaction.h +++ b/libnymea-app/types/ruleaction.h @@ -39,6 +39,7 @@ class RuleActionParams; class RuleAction : public QObject { Q_OBJECT + Q_PROPERTY(QUuid thingId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) Q_PROPERTY(QUuid deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged) Q_PROPERTY(QUuid actionTypeId READ actionTypeId WRITE setActionTypeId NOTIFY actionTypeIdChanged) Q_PROPERTY(QString interfaceName READ interfaceName WRITE setInterfaceName NOTIFY interfaceNameChanged) diff --git a/libnymea-app/types/tag.cpp b/libnymea-app/types/tag.cpp index 47f363dc..1de78786 100644 --- a/libnymea-app/types/tag.cpp +++ b/libnymea-app/types/tag.cpp @@ -40,14 +40,14 @@ Tag::Tag(const QString &tagId, const QString &value, QObject *parent): } -QUuid Tag::deviceId() const +QUuid Tag::thingId() const { - return m_deviceId; + return m_thingId; } -void Tag::setDeviceId(const QUuid &deviceId) +void Tag::setThingId(const QUuid &thingId) { - m_deviceId = deviceId; + m_thingId = thingId; } QUuid Tag::ruleId() const @@ -74,7 +74,7 @@ void Tag::setValue(const QString &value) { if (m_value != value) { m_value = value; - qDebug() << "tags value changed" << m_deviceId << m_tagId << value; + qDebug() << "tags value changed" << m_thingId.toString() << m_tagId << value; emit valueChanged(); } } diff --git a/libnymea-app/types/tag.h b/libnymea-app/types/tag.h index d58937fe..f38efd96 100644 --- a/libnymea-app/types/tag.h +++ b/libnymea-app/types/tag.h @@ -37,7 +37,8 @@ class Tag : public QObject { Q_OBJECT - Q_PROPERTY(QUuid deviceId READ deviceId CONSTANT) + Q_PROPERTY(QUuid thingId READ thingId CONSTANT) + Q_PROPERTY(QUuid deviceId READ thingId CONSTANT) Q_PROPERTY(QUuid ruleId READ ruleId CONSTANT) Q_PROPERTY(QString tagId READ tagId CONSTANT) Q_PROPERTY(QString value READ value NOTIFY valueChanged) @@ -45,8 +46,8 @@ class Tag : public QObject public: explicit Tag(const QString &tagId, const QString &value, QObject *parent = nullptr); - QUuid deviceId() const; - void setDeviceId(const QUuid &deviceId); + QUuid thingId() const; + void setThingId(const QUuid &thingId); QUuid ruleId() const; void setRuleId(const QUuid &ruleId); @@ -60,7 +61,7 @@ signals: void valueChanged(); private: - QUuid m_deviceId; + QUuid m_thingId; QUuid m_ruleId; QString m_tagId; QString m_value; diff --git a/libnymea-app/types/tags.cpp b/libnymea-app/types/tags.cpp index b55cfa9a..90db8f86 100644 --- a/libnymea-app/types/tags.cpp +++ b/libnymea-app/types/tags.cpp @@ -47,8 +47,9 @@ int Tags::rowCount(const QModelIndex &parent) const QVariant Tags::data(const QModelIndex &index, int role) const { switch (role) { + case RoleThingId: case RoleDeviceId: - return m_list.at(index.row())->deviceId(); + return m_list.at(index.row())->thingId(); case RoleRuleId: return m_list.at(index.row())->ruleId(); case RoleTagId: @@ -62,6 +63,7 @@ QVariant Tags::data(const QModelIndex &index, int role) const QHash Tags::roleNames() const { QHash roles; + roles.insert(RoleThingId, "thingId"); roles.insert(RoleDeviceId, "deviceId"); roles.insert(RoleRuleId, "ruleId"); roles.insert(RoleTagId, "tagId"); @@ -111,19 +113,27 @@ void Tags::removeTag(Tag *tag) Tag *Tags::get(int index) const { + if (index < 0 || index >= m_list.count()) { + return nullptr; + } return m_list.at(index); } -Tag *Tags::findDeviceTag(const QUuid &deviceId, const QString &tagId) const +Tag *Tags::findThingTag(const QUuid &thingId, const QString &tagId) const { foreach (Tag *tag, m_list) { - if (tag->deviceId() == deviceId && tag->tagId() == tagId) { + if (tag->thingId() == thingId && tag->tagId() == tagId) { return tag; } } return nullptr; } +Tag *Tags::findDeviceTag(const QUuid &deviceId, const QString &tagId) const +{ + return findThingTag(deviceId, tagId); +} + Tag *Tags::findRuleTag(const QString &ruleId, const QString &tagId) const { foreach (Tag *tag, m_list) { diff --git a/libnymea-app/types/tags.h b/libnymea-app/types/tags.h index d6a29db5..ba573eb3 100644 --- a/libnymea-app/types/tags.h +++ b/libnymea-app/types/tags.h @@ -41,6 +41,7 @@ class Tags: public QAbstractListModel Q_PROPERTY(int count READ rowCount NOTIFY countChanged) public: enum Roles { + RoleThingId, RoleDeviceId, RoleRuleId, RoleTagId, @@ -58,8 +59,9 @@ public: void addTags(QList tags); void removeTag(Tag *tag); - Tag* get(int index) const; + Q_INVOKABLE Tag* get(int index) const; + Q_INVOKABLE Tag* findThingTag(const QUuid &thingId, const QString &tagId) const; Q_INVOKABLE Tag* findDeviceTag(const QUuid &deviceId, const QString &tagId) const; Q_INVOKABLE Tag* findRuleTag(const QString &ruleId, const QString &tagId) const; diff --git a/nymea-app/ruletemplates.qrc b/nymea-app/ruletemplates.qrc index e86cba88..ca9f271b 100644 --- a/nymea-app/ruletemplates.qrc +++ b/nymea-app/ruletemplates.qrc @@ -9,5 +9,7 @@ ruletemplates/thermostattemplates.json ruletemplates/mediatemplates.json ruletemplates/doorbellruletemplates.json + ruletemplates/irrigationtemplates.json + ruletemplates/lighttemplates.json diff --git a/nymea-app/ruletemplates/irrigationtemplates.json b/nymea-app/ruletemplates/irrigationtemplates.json new file mode 100644 index 00000000..81e33629 --- /dev/null +++ b/nymea-app/ruletemplates/irrigationtemplates.json @@ -0,0 +1,46 @@ +{ + "templates": [ + { + "description": "Schedule an irrigation", + "ruleNameTemplate": "Schedule for %0", + "timeDescriptorTemplate": { + "calendarItemTemplates": [ + { + "startTime": "07:00", + "duration": 20, + "repeatingOption": { + "repeatingMode": "RepeatingModeDaily" + }, + "editable": true + } + ] + }, + "ruleActionTemplates": [ + { + "interfaceName": "irrigation", + "interfaceAction": "power", + "selectionId": 0, + "params": [ + { + "name": "power", + "value": true + } + ] + } + ], + "ruleExitActionTemplates": [ + { + "interfaceName": "irrigation", + "interfaceAction": "power", + "selectionId": 0, + "params": [ + { + "name": "power", + "value": false + } + ] + } + ] + } + ] +} diff --git a/nymea-app/ruletemplates/lighttemplates.json b/nymea-app/ruletemplates/lighttemplates.json new file mode 100644 index 00000000..f3b5ce45 --- /dev/null +++ b/nymea-app/ruletemplates/lighttemplates.json @@ -0,0 +1,38 @@ +{ + "templates": [ + { + "description": "Wake up with light", + "ruleNameTemplate": "Wake up with %0", + "timeDescriptorTemplate": { + "timeEventItemTemplates": [ + { + "time": "07:00", + "repeatingOption": { + "repeatingMode": "RepeatingModeWeekly", + "weekDays": [1, 2, 3, 4, 5] + }, + "editable": true + } + ] + }, + "ruleActionTemplates": [ + { + "interfaceName": "dimmablelight", + "interfaceAction": "power", + "selectionId": 0, + "params": [ + { + "name": "power", + "value": true + } + ] + }, + { + "interfaceName": "dimmablelight", + "interfaceAction": "brightness", + "selectionId": 0 + } + ] + } + ] +} diff --git a/nymea-app/ui/devicepages/IrrigationDevicePage.qml b/nymea-app/ui/devicepages/IrrigationDevicePage.qml index 31ea182f..f38c5bff 100644 --- a/nymea-app/ui/devicepages/IrrigationDevicePage.qml +++ b/nymea-app/ui/devicepages/IrrigationDevicePage.qml @@ -38,41 +38,141 @@ import "../components" DevicePageBase { id: root - readonly property var powerStateType: deviceClass.stateTypes.findByName("power") - readonly property var powerState: device.states.getState(powerStateType.id) - readonly property var powerActionType: deviceClass.actionTypes.findByName("power"); + readonly property var powerStateType: thing.thingClass.stateTypes.findByName("power") + readonly property var powerState: thing.states.getState(powerStateType.id) + readonly property var powerActionType: thing.thingClass.actionTypes.findByName("power"); + + readonly property bool isOn: powerState && powerState.value === true + + QtObject { + id: d + property var pendingRuleCreationId: -1 + property var now: new Date() + } + Timer { + running: true + repeat: true + interval: 5000 + onTriggered: { + d.now = new Date() + } + } + + Component.onCompleted: { + cleanupRules(); + } + + function waterFor(minutes) { + var rule = engine.ruleManager.createNewRule(); + var now = new Date(); + var offDate = new Date(now.getTime() + (minutes * 60000)); + + rule.name = qsTr("Turn %1 off at %2").arg(root.thing.name).arg(Qt.formatDateTime(offDate)) + + var timeEvent = rule.timeDescriptor.timeEventItems.createNewTimeEventItem(); + timeEvent.dateTime = offDate; + timeEvent.repeatingOption.repeatingMode = RepeatingOption.RepeatingModeNone; + rule.timeDescriptor.timeEventItems.addTimeEventItem(timeEvent); + + var ruleAction = rule.actions.createNewRuleAction(); + ruleAction.thingId = root.thing.id; + ruleAction.interfaceName = "power"; + ruleAction.interfaceAction = "power" + ruleAction.ruleActionParams.setRuleActionParamByName("power", false); + rule.actions.addRuleAction(ruleAction) + + d.pendingRuleCreationId = engine.ruleManager.addRule(rule); + } + + function cleanupRules() { + print("cleaning up stale oneshot watering rules") + for (var i = 0; i < engine.tagsManager.tags.count; i++) { + var tag = engine.tagsManager.tags.get(i); + if (tag.tagId === "oneshot-watering") { + print("have a oneshot-watering tag") + // Delete it if the timer expired already + var rule = engine.ruleManager.rules.getRule(tag.ruleId) + if (rule.timeDescriptor.timeEventItems.get(0).dateTime < new Date()) { + print("need to cleanup rule:", tag.ruleId, rule.timeDescriptor.timeEventItems.get(0).dateTime) + engine.ruleManager.removeRule(tag.ruleId) + } else { + print("Rule still pending:", tag.ruleId) + } + // Also delete it if the irrigation is off already + if (root.powerState.value === false) { + engine.ruleManager.removeRule(tag.ruleId); + } + } + } + } + + Connections { + target: engine.ruleManager + onAddRuleReply: { + if (commandId == d.pendingRuleCreationId) { + d.pendingRuleCreationId = -1 + if (ruleError != "RuleErrorNoError") { + var comp = Qt.createComponent("../components/ErrorDialog.qml") + var popup = comp.createObject(app, {errorCode: ruleError}) + popup.open(); + return; + } + // Tag the rule so we can clean identify it + engine.tagsManager.tagRule(ruleId, "oneshot-watering", root.thing.id) + + // Off rule has been added. Turning on now + if (root.powerState.value === false) { + engine.thingManager.executeAction(root.thing.id, root.powerActionType.id, [{paramTypeId: root.powerActionType.id, value: true}]) + } + } + } + } + + Connections { + target: root.powerState + onValueChanged: cleanupRules() + } + + LogsModelNg { + id: history + engine: _engine + thingId: root.thing.id + typeIds: [root.powerStateType.id] + property var lastWatering: count > 0 ? get(0).timestamp : null + live: true + } + + TagsProxyModel { + id: tagsProxy + tags: engine.tagsManager.tags + filterTagId: "oneshot-watering" + filterValue: root.thing.id + } GridLayout { + id: mainGrid anchors.fill: parent anchors.margins: app.margins columns: app.landscape ? 2 : 1 - rowSpacing: app.margins - columnSpacing: app.margins - Layout.alignment: Qt.AlignCenter Item { - Layout.preferredWidth: Math.max(app.iconSize * 6, parent.width / 5) - Layout.preferredHeight: width - Layout.topMargin: app.margins - Layout.bottomMargin: app.landscape ? app.margins : 0 - Layout.alignment: Qt.AlignCenter - Layout.rowSpan: app.landscape ? 4 : 1 - Layout.fillHeight: true + Layout.preferredWidth: app.landscape ? parent.width * .4 : parent.width + Layout.preferredHeight: app.landscape ? parent.height : parent.height *.4 AbstractButton { - height: Math.min(parent.height, parent.width) + height: Math.min(Math.min(parent.height, parent.width), app.iconSize * 5) width: height anchors.centerIn: parent Rectangle { anchors.fill: parent color: "transparent" - border.color: root.powerState.value === true ? app.accentColor : bulbIcon.keyColor + border.color: root.powerState.value === true ? app.accentColor : irrigationIcon.keyColor border.width: 4 radius: width / 2 } ColorIcon { - id: bulbIcon + id: irrigationIcon anchors.fill: parent anchors.margins: app.margins * 1.5 name: "../images/irrigation.svg" @@ -84,9 +184,157 @@ DevicePageBase { param["paramTypeId"] = root.powerActionType.paramTypes.get(0).id; param["value"] = !root.powerState.value; params.push(param) - engine.deviceManager.executeAction(root.device.id, root.powerStateType.id, params); + engine.thingManager.executeAction(root.device.id, root.powerStateType.id, params); + PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection) } } } + + ColumnLayout { + Layout.preferredWidth: app.landscape ? parent.width * .6 : parent.width + Layout.preferredHeight: app.landscape ? parent.height : parent.height * .6 + Item { Layout.fillWidth: true; Layout.fillHeight: true } + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + horizontalAlignment: Text.AlignHCenter + text: root.isOn ? qsTr("Watering since") + : history.lastWatering ? qsTr("Last watering") + : qsTr("This irrigation has not been used yet") + } + + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + horizontalAlignment: Text.AlignHCenter + text: history.lastWatering ? Qt.formatDateTime(history.lastWatering) : "" + font.pixelSize: app.largeFont + color: app.accentColor + } + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins + horizontalAlignment: Text.AlignHCenter + font.pixelSize: app.smallFont + text: { + if (!history.lastWatering) { + return "" + } + + var n = Math.floor((d.now - history.lastWatering) / 60 / 1000) + + n = Math.max(0, n) + + if (root.isOn) { + if (n < 60) { + return qsTr("%n minute(s)", "", n); + } + n /= 60; + if (n < 24) { + return qsTr("%n hour(s)", "", n); + } + n /= 24; + return qsTr("%n day(s)", "", n); + + } + + if (n < 60) { + return qsTr("%n minute(s) ago", "", n); + } + n /= 60; + if (n < 24) { + return qsTr("%n hour(s) ago", "", n); + } + n /= 24; + return qsTr("%n day(s) ago", "", n); + } + } + + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.topMargin: app.margins + horizontalAlignment: Text.AlignHCenter + text: tagsProxy.count > 0 ? + //: Irrigation will be turned of at, e.g. 09:00 + qsTr("Watering until") + //: Turn on irrigation for, e.g. 5 minutes + : root.isOn ? qsTr("Turn off in") : qsTr("Water for") + } + + GridLayout { + columns: 3 + visible: tagsProxy.count == 0 + + Button { + Layout.fillWidth: true + text: qsTr("1 minute") + onClicked: root.waterFor(1) + } + Button { + Layout.fillWidth: true + text: qsTr("2 minutes") + onClicked: root.waterFor(2) + } + Button { + Layout.fillWidth: true + text: qsTr("5 minutes") + onClicked: root.waterFor(5) + } + Button { + Layout.fillWidth: true + text: qsTr("15 minutes") + onClicked: root.waterFor(15) + } + Button { + Layout.fillWidth: true + text: qsTr("30 minutes") + onClicked: root.waterFor(30) + } + Button { + Layout.fillWidth: true + text: qsTr("1 hour") + onClicked: root.waterFor(60) + } + } + + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins + horizontalAlignment: Text.AlignHCenter + visible: tagsProxy.count > 0 + font.pixelSize: app.largeFont + color: app.accentColor + text: tagsProxy.count == 0 ? "" : Qt.formatDateTime(engine.ruleManager.rules.getRule(tagsProxy.get(0).ruleId).timeDescriptor.timeEventItems.get(0).dateTime) + } + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins + horizontalAlignment: Text.AlignHCenter + visible: tagsProxy.count > 0 + font.pixelSize: app.smallFont + text: { + if (tagsProxy.count == 0) { + return "" + } + + var end = engine.ruleManager.rules.getRule(tagsProxy.get(0).ruleId).timeDescriptor.timeEventItems.get(0).dateTime + var n = Math.floor((end - d.now) / 60 / 1000) + + n = Math.max(0, n); + + if (n < 60) { + return qsTr("%n minute(s) left", "", n); + } + n /= 60; + if (n < 24) { + return qsTr("%n hour(s) left", "", n); + } + n /= 24; + return qsTr("%n day(s) left", "", n); + } + } + + Item { Layout.fillWidth: true; Layout.fillHeight: true } + } } } diff --git a/nymea-app/ui/magic/EditRulePage.qml b/nymea-app/ui/magic/EditRulePage.qml index 8304bb33..6dd2d3c5 100644 --- a/nymea-app/ui/magic/EditRulePage.qml +++ b/nymea-app/ui/magic/EditRulePage.qml @@ -43,9 +43,9 @@ Page { property bool busy: false readonly property bool isEventBased: rule.eventDescriptors.count > 0 || rule.timeDescriptor.timeEventItems.count > 0 - readonly property bool isStateBased: (rule.stateEvaluator !== null || rule.timeDescriptor.calendarItems.count > 0) && !isEventBased + readonly property bool isStateBased: (rule.stateEvaluator !== null || rule.timeDescriptor.calendarItems.count > 0) readonly property bool actionsVisible: true - readonly property bool exitActionsVisible: (engine.jsonRpcClient.ensureServerVersion(1.7) && !isEmpty) || isStateBased + readonly property bool exitActionsVisible: engine.jsonRpcClient.ensureServerVersion("1.7") && isStateBased readonly property bool hasActions: rule.actions.count > 0 readonly property bool hasExitActions: rule.exitActions.count > 0 readonly property bool isEmpty: !isEventBased && !isStateBased && !hasActions @@ -445,12 +445,10 @@ Page { } } } - - } } - ThinDivider { visible: !root.isStateBased } + ThinDivider { visible: root.isEmpty || root.isEventBased } Label { Layout.fillWidth: true @@ -460,7 +458,7 @@ Page { text: eventsRepeater.count === 0 && timeEventRepeater.count === 0 && actionsRepeater.count === 0 ? qsTr("Execute actions when something happens.") : qsTr("When any of these events happen...") - visible: !root.isStateBased + visible: root.isEmpty || root.isEventBased font.bold: true } Label { @@ -502,7 +500,7 @@ Page { Layout.fillWidth: true Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins text: eventsRepeater.count == 0 && timeEventRepeater.count === 0 ? qsTr("Configure...") : qsTr("Add another...") - visible: !root.isStateBased + visible: root.isEmpty || root.isEventBased onClicked: { if (root.initialDeviceToBeAdded !== null) { var eventDescriptor = root.rule.eventDescriptors.createNewEventDescriptor(); @@ -617,10 +615,10 @@ Page { ThinDivider { visible: root.actionsVisible } Label { - text: root.isEmpty ? qsTr("Create a scene.") : - root.isStateBased ? - (root.rule.stateEvaluator === 0 ? qsTr("...come true, execute those actions:") : qsTr("...comes true, execute those actions:")) : - qsTr("...execute those actions:") + text: root.isEmpty ? qsTr("Create a scene.") + : root.isEventBased ? qsTr("...execute those actions:") + : root.isStateBased ? qsTr("...come true, execute those actions:") + : qsTr("Execute those actions:") font.pixelSize: app.mediumFont Layout.fillWidth: true Layout.margins: app.margins @@ -674,8 +672,8 @@ Page { ThinDivider { visible: root.exitActionsVisible } Label { - text: root.isStateBased ? qsTr("...isn't met any more, execute those actions:") : - qsTr("If the condition isn't met, execute those actions instead:") + text: root.isEventBased ? qsTr("If the condition isn't met, execute those actions instead:") : qsTr("When the condition isn't met any more, execute those actions:") + Layout.fillWidth: true Layout.margins: app.margins wrapMode: Text.WordWrap @@ -699,7 +697,7 @@ Page { Button { Layout.fillWidth: true Layout.leftMargin: app.margins; Layout.rightMargin: app.margins; Layout.bottomMargin: app.margins - text: actionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...") + text: exitActionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...") onClicked: { var page = pageStack.push(ruleActionQuestionPageComponent, {exitAction: true}); } diff --git a/nymea-app/ui/magic/NewThingMagicPage.qml b/nymea-app/ui/magic/NewThingMagicPage.qml index 70282573..9618c217 100644 --- a/nymea-app/ui/magic/NewThingMagicPage.qml +++ b/nymea-app/ui/magic/NewThingMagicPage.qml @@ -93,6 +93,48 @@ Page { return; } + // Fill in TimeDescriptor + if (ruleTemplate.timeDescriptorTemplate !== null) { + print("RuleFromTemplate: Filling timeDescriptor.", rule.timeDescriptor.calendarItems.count, ruleTemplate.timeDescriptorTemplate.calendarItemTemplates.count); + for (var i = rule.timeDescriptor.calendarItems.count; i < ruleTemplate.timeDescriptorTemplate.calendarItemTemplates.count; i++) { + print("Need more CalendarItems"); + var calendarItemTemplate = ruleTemplate.timeDescriptorTemplate.calendarItemTemplates.get(i); + var calendarItem = calendarItemTemplate.createCalendarItem(); + if (!calendarItemTemplate.editable) { + rule.timeDescriptor.calendarItems.addCalendarItem(calendarItem); + fillRuleFromTemplate(rule, ruleTemplate); + return; + } + + var page = pageStack.push(Qt.resolvedUrl("EditCalendarItemPage.qml"), {calendarItem: calendarItem}) + page.done.connect(function() { + rule.timeDescriptor.calendarItems.addCalendarItem(calendarItem); + fillRuleFromTemplate(rule, ruleTemplate); + }); + page.backPressed.connect(function() {rule.destroy(); root.done();}); + return; + } + + for (var i = rule.timeDescriptor.timeEventItems.count; i < ruleTemplate.timeDescriptorTemplate.timeEventItemTemplates.count; i++) { + print("Need more TimeEventItems"); + var timeEventItemTemplate = ruleTemplate.timeDescriptorTemplate.timeEventItemTemplates.get(i); + var timeEventItem = timeEventItemTemplate.createTimeEventItem(); + if (!timeEventItemTemplate.editable) { + rule.timeDescriptor.timeEventItems.addTimeEventItem(timeEventItem); + fillRuleFromTemplate(rule, ruleTemplate); + return; + } + + var page = pageStack.push(Qt.resolvedUrl("EditTimeEventItemPage.qml"), {timeEventItem: timeEventItem}); + page.done.connect(function() { + rule.timeDescriptor.timeEventItems.addTimeEventItem(timeEventItem); + fillRuleFromTemplate(rule, ruleTemplate); + }) + page.backPressed.connect(function() {rule.destroy(); root.done()}); + return; + } + } + // Fill in StateEvaluator if (ruleTemplate.stateEvaluatorTemplate !== null) { print("RuleFromTemplate: Filling stateEvaluator")