Merge remote-tracking branch 'origin/interfaced-based-rules' into landing-silo

This commit is contained in:
Jenkins 2018-06-21 16:32:45 +02:00
commit aae0d3584a
67 changed files with 2439 additions and 845 deletions

View File

@ -224,7 +224,7 @@ void DeviceManager::getConfiguredDevicesResponse(const QVariantMap &params)
// set initial state values
QVariantList stateVariantList = deviceVariant.toMap().value("states").toList();
foreach (const QVariant &stateMap, stateVariantList) {
QUuid stateTypeId = stateMap.toMap().value("stateTypeId").toUuid();
QString stateTypeId = stateMap.toMap().value("stateTypeId").toString();
StateType *st = dc->stateTypes()->getStateType(stateTypeId);
if (!st) {
qWarning() << "Can't find a statetype for this state";

View File

@ -134,6 +134,21 @@ bool JsonRpcClient::pushButtonAuthAvailable() const
return m_pushButtonAuthAvailable;
}
QString JsonRpcClient::serverVersion() const
{
return m_serverVersion;
}
QString JsonRpcClient::jsonRpcVersion() const
{
return m_jsonRpcVersion.toString();
}
QString JsonRpcClient::serverUuid() const
{
return m_serverUuid;
}
int JsonRpcClient::createUser(const QString &username, const QString &password)
{
QVariantMap params;
@ -168,6 +183,10 @@ int JsonRpcClient::requestPushButtonAuth(const QString &deviceName)
return reply->commandId();
}
bool JsonRpcClient::ensureServerVersion(const QString &jsonRpcVersion)
{
return QVersionNumber(m_jsonRpcVersion) >= QVersionNumber::fromString(jsonRpcVersion);
}
void JsonRpcClient::processAuthenticate(const QVariantMap &data)
{
@ -224,7 +243,7 @@ void JsonRpcClient::sendRequest(const QVariantMap &request)
{
QVariantMap newRequest = request;
newRequest.insert("token", m_token);
// qDebug() << "Sending request" << qUtf8Printable(QJsonDocument::fromVariant(newRequest).toJson());
qDebug() << "Sending request" << qUtf8Printable(QJsonDocument::fromVariant(newRequest).toJson());
m_connection->sendData(QJsonDocument::fromVariant(newRequest).toJson());
}
@ -259,7 +278,7 @@ void JsonRpcClient::dataReceived(const QByteArray &data)
// qWarning() << "Could not parse json data from mea" << data << error.errorString();
return;
}
// qDebug() << "received response" << m_receiveBuffer.left(splitIndex);
qDebug() << "received response" << m_receiveBuffer.left(splitIndex);
m_receiveBuffer = m_receiveBuffer.right(m_receiveBuffer.length() - splitIndex - 1);
if (!m_receiveBuffer.isEmpty()) {
staticMetaObject.invokeMethod(this, "dataReceived", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray()));
@ -274,24 +293,28 @@ void JsonRpcClient::dataReceived(const QByteArray &data)
m_initialSetupRequired = dataMap.value("initialSetupRequired").toBool();
m_authenticationRequired = dataMap.value("authenticationRequired").toBool();
m_pushButtonAuthAvailable = dataMap.value("pushButtonAuthAvailable").toBool();
emit pushButtonAuthAvailableChanged();
qDebug() << "Handshake received" << "initRequired:" << m_initialSetupRequired << "authRequired:" << m_authenticationRequired << "pushButtonAvailable:" << m_pushButtonAuthAvailable;;
m_serverUuid = dataMap.value("uuid").toString();
emit pushButtonAuthAvailableChanged();
m_serverVersion = dataMap.value("version").toString();
QString protoVersionString = dataMap.value("protocol version").toString();
if (!protoVersionString.contains('.')) {
protoVersionString.prepend("0.");
}
m_jsonRpcVersion = QVersionNumber::fromString(protoVersionString);
QVersionNumber minimumRequiredVersion = QVersionNumber(1, 0);
QVersionNumber protocolVersion = QVersionNumber::fromString(protoVersionString);
if (protocolVersion < minimumRequiredVersion) {
qWarning() << "Nymea box doesn't support minimum required version. Required:" << minimumRequiredVersion << "Found:" << protocolVersion;
if (m_jsonRpcVersion < minimumRequiredVersion) {
qWarning() << "Nymea box doesn't support minimum required version. Required:" << minimumRequiredVersion << "Found:" << m_jsonRpcVersion;
m_connection->disconnect();
emit invalidProtocolVersion(protocolVersion.toString(), minimumRequiredVersion.toString());
emit invalidProtocolVersion(m_jsonRpcVersion.toString(), minimumRequiredVersion.toString());
return;
}
emit handshakeReceived();
if (m_initialSetupRequired) {
emit initialSetupRequiredChanged();
return;

View File

@ -24,6 +24,7 @@
#include <QObject>
#include <QVariantMap>
#include <QPointer>
#include <QVersionNumber>
#include "nymeaconnection.h"
#include "jsonhandler.h"
@ -39,6 +40,9 @@ class JsonRpcClient : public JsonHandler
Q_PROPERTY(bool initialSetupRequired READ initialSetupRequired NOTIFY initialSetupRequiredChanged)
Q_PROPERTY(bool authenticationRequired READ authenticationRequired NOTIFY authenticationRequiredChanged)
Q_PROPERTY(bool pushButtonAuthAvailable READ pushButtonAuthAvailable NOTIFY pushButtonAuthAvailableChanged)
Q_PROPERTY(QString serverVersion READ serverVersion NOTIFY handshakeReceived)
Q_PROPERTY(QString jsonRpcVersion READ jsonRpcVersion NOTIFY handshakeReceived)
Q_PROPERTY(QString serverUuid READ serverUuid NOTIFY handshakeReceived)
public:
explicit JsonRpcClient(NymeaConnection *connection, QObject *parent = 0);
@ -56,13 +60,19 @@ public:
bool authenticationRequired() const;
bool pushButtonAuthAvailable() const;
QString serverVersion() const;
QString jsonRpcVersion() const;
QString serverUuid() const;
// ui methods
Q_INVOKABLE int createUser(const QString &username, const QString &password);
Q_INVOKABLE int authenticate(const QString &username, const QString &password, const QString &deviceName);
Q_INVOKABLE int requestPushButtonAuth(const QString &deviceName);
Q_INVOKABLE bool ensureServerVersion(const QString &jsonRpcVersion);
signals:
void handshakeReceived();
void initialSetupRequiredChanged();
void authenticationRequiredChanged();
void pushButtonAuthAvailableChanged();
@ -94,6 +104,8 @@ private:
bool m_pushButtonAuthAvailable = false;
int m_pendingPushButtonTransaction = -1;
QString m_serverUuid;
QVersionNumber m_jsonRpcVersion;
QString m_serverVersion;
QByteArray m_token;
QByteArray m_receiveBuffer;

View File

@ -152,7 +152,7 @@ ParamType *JsonTypes::unpackParamType(const QVariantMap &paramTypeMap, QObject *
StateType *JsonTypes::unpackStateType(const QVariantMap &stateTypeMap, QObject *parent)
{
StateType *stateType = new StateType(parent);
stateType->setId(stateTypeMap.value("id").toUuid());
stateType->setId(stateTypeMap.value("id").toString());
stateType->setName(stateTypeMap.value("name").toString());
stateType->setDisplayName(stateTypeMap.value("displayName").toString());
stateType->setIndex(stateTypeMap.value("index").toInt());
@ -267,18 +267,29 @@ QVariantList JsonTypes::packRuleActions(RuleActions *ruleActions)
QVariantList ret;
for (int i = 0; i < ruleActions->rowCount(); i++) {
QVariantMap ruleAction;
ruleAction.insert("actionTypeId", ruleActions->get(i)->actionTypeId());
ruleAction.insert("deviceId", ruleActions->get(i)->deviceId());
if (ruleActions->get(i)->ruleActionParams()->rowCount() > 0) {
RuleAction *ra = ruleActions->get(i);
if (!ra->actionTypeId().isNull() && !ra->deviceId().isNull()) {
ruleAction.insert("deviceId", ra->deviceId());
ruleAction.insert("actionTypeId", ra->actionTypeId());
} else {
ruleAction.insert("interface", ra->interfaceName());
ruleAction.insert("interfaceAction", ra->interfaceAction());
}
if (ra->ruleActionParams()->rowCount() > 0) {
QVariantList ruleActionParams;
for (int j = 0; j < ruleActions->get(i)->ruleActionParams()->rowCount(); j++) {
for (int j = 0; j < ra->ruleActionParams()->rowCount(); j++) {
QVariantMap ruleActionParam;
ruleActionParam.insert("paramTypeId", ruleActions->get(i)->ruleActionParams()->get(j)->paramTypeId());
if (!ruleActions->get(i)->ruleActionParams()->get(j)->eventTypeId().isEmpty() && !ruleActions->get(i)->ruleActionParams()->get(j)->eventParamTypeId().isEmpty()) {
ruleActionParam.insert("eventTypeId", ruleActions->get(i)->ruleActionParams()->get(j)->eventTypeId());
ruleActionParam.insert("eventParamTypeId", ruleActions->get(i)->ruleActionParams()->get(j)->eventParamTypeId());
RuleActionParam *rap = ruleActions->get(i)->ruleActionParams()->get(j);
if (!rap->paramTypeId().isNull()) {
ruleActionParam.insert("paramTypeId", rap->paramTypeId());
} else {
ruleActionParam.insert("value", ruleActions->get(i)->ruleActionParams()->get(j)->value());
ruleActionParam.insert("paramName", rap->paramName());
}
if (!rap->eventTypeId().isEmpty() && !rap->eventParamTypeId().isEmpty()) {
ruleActionParam.insert("eventTypeId", rap->eventTypeId());
ruleActionParam.insert("eventParamTypeId", rap->eventParamTypeId());
} else {
ruleActionParam.insert("value", rap->value());
}
ruleActionParams.append(ruleActionParam);
}
@ -307,7 +318,11 @@ QVariantList JsonTypes::packEventDescriptors(EventDescriptors *eventDescriptors)
QVariantList paramDescriptors;
for (int j = 0; j < eventDescriptor->paramDescriptors()->rowCount(); j++) {
QVariantMap paramDescriptor;
paramDescriptor.insert("paramTypeId", eventDescriptor->paramDescriptors()->get(j)->paramTypeId());
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::ValueOperator>();
paramDescriptor.insert("operator", operatorEnum.valueToKey(eventDescriptor->paramDescriptors()->get(j)->operatorType()));
@ -334,10 +349,15 @@ QVariantMap JsonTypes::packStateEvaluator(StateEvaluator *stateEvaluator)
QMetaEnum stateOperatorEnum = QMetaEnum::fromType<StateEvaluator::StateOperator>();
ret.insert("operator", stateOperatorEnum.valueToKey(stateEvaluator->stateOperator()));
QVariantMap stateDescriptor;
stateDescriptor.insert("deviceId", stateEvaluator->stateDescriptor()->deviceId());
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::ValueOperator>();
stateDescriptor.insert("operator", valueOperatorEnum.valueToKeys(stateEvaluator->stateDescriptor()->valueOperator()));
stateDescriptor.insert("stateTypeId", stateEvaluator->stateDescriptor()->stateTypeId());
stateDescriptor.insert("value", stateEvaluator->stateDescriptor()->value());
ret.insert("stateDescriptor", stateDescriptor);
QVariantList childEvaluators;

View File

@ -34,6 +34,7 @@
#include "models/logsmodelng.h"
#include "models/valuelogsproxymodel.h"
#include "models/eventdescriptorparamsfiltermodel.h"
#include "models/interfacesproxy.h"
#include "basicconfiguration.h"
#include "wifisetup/networkmanagercontroler.h"
@ -119,6 +120,7 @@ void registerQmlTypes() {
qmlRegisterUncreatableType<Interface>(uri, 1, 0, "Interface", "Uncreatable");
qmlRegisterSingletonType<Interfaces>(uri, 1, 0, "Interfaces", interfacesModel_provider);
qmlRegisterType<InterfacesProxy>(uri, 1, 0, "InterfacesProxy");
qmlRegisterUncreatableType<Plugin>(uri, 1, 0, "Plugin", "Can't create this in QML. Get it from the Plugins.");
qmlRegisterUncreatableType<Plugins>(uri, 1, 0, "Plugins", "Can't create this in QML. Get it from the DeviceManager.");

View File

@ -56,7 +56,8 @@ SOURCES += \
wifisetup/wirelessaccesspoints.cpp \
wifisetup/wirelesssetupmanager.cpp \
wifisetup/networkmanagercontroler.cpp \
models/logsmodelng.cpp
models/logsmodelng.cpp \
models/interfacesproxy.cpp
HEADERS += \
engine.h \
@ -99,7 +100,8 @@ HEADERS += \
wifisetup/wirelesssetupmanager.h \
wifisetup/networkmanagercontroler.h \
libmea-core.h \
models/logsmodelng.h
models/logsmodelng.h \
models/interfacesproxy.h
unix {
target.path = /usr/lib

View File

@ -0,0 +1,113 @@
#include "interfacesproxy.h"
#include "types/interface.h"
#include "types/interfaces.h"
#include "types/device.h"
#include "devices.h"
#include "engine.h"
InterfacesProxy::InterfacesProxy(QObject *parent): QSortFilterProxyModel(parent)
{
m_interfaces = new Interfaces(this);
setSourceModel(m_interfaces);
}
bool InterfacesProxy::showEvents() const
{
return m_showEvents;
}
void InterfacesProxy::setShowEvents(bool showEvents)
{
if (m_showEvents != showEvents) {
m_showEvents = showEvents;
emit showEventsChanged();
invalidateFilter();
}
}
bool InterfacesProxy::showActions() const
{
return m_showActions;
}
void InterfacesProxy::setShowActions(bool showActions)
{
if (m_showActions != showActions) {
m_showActions = showActions;
emit showActionsChanged();
invalidateFilter();
}
}
bool InterfacesProxy::showStates() const
{
return m_showStates;
}
void InterfacesProxy::setShowStates(bool showStates)
{
if (m_showStates != showStates) {
m_showStates = showStates;
emit showStatesChanged();
invalidateFilter();
}
}
bool InterfacesProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
Q_UNUSED(source_parent)
qDebug() << "filterAcceptsRow";
QString interfaceName = m_interfaces->get(source_row)->name();
if (!m_shownInterfaces.isEmpty()) {
if (!m_shownInterfaces.contains(interfaceName)) {
return false;
}
}
if (m_devicesFilter != nullptr) {
// TODO: This could be improved *a lot* by caching interfaces in the devices model...
bool found = false;
for (int i = 0; i < m_devicesFilter->rowCount(); i++) {
Device *d = m_devicesFilter->get(i);
DeviceClass *dc = Engine::instance()->deviceManager()->deviceClasses()->getDeviceClass(d->deviceClassId());
if (!dc) {
qWarning() << "Cannot find DeviceClass for device:" << d->id() << d->name();
return false;
}
if (dc->interfaces().contains(interfaceName)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
Interface* iface = m_interfaces->get(source_row);
if (m_showEvents) {
if (iface->eventTypes()->rowCount() > 0) {
return true;
}
}
if (m_showActions) {
if (iface->actionTypes()->rowCount() > 0) {
return true;
}
}
if (m_showStates) {
if (iface->stateTypes()->rowCount() > 0) {
return true;
}
}
return false;
}
Interface *InterfacesProxy::get(int index) const
{
return m_interfaces->get(mapToSource(this->index(index, 0)).row());
}

View File

@ -0,0 +1,58 @@
#ifndef INTERFACESPROXY_H
#define INTERFACESPROXY_H
#include <QSortFilterProxyModel>
class Devices;
class Interface;
class Interfaces;
class InterfacesProxy: public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged)
Q_PROPERTY(Devices* devicesFilter READ devicesFilter WRITE setDevicesFilter NOTIFY devicesFilterChanged)
Q_PROPERTY(bool showEvents READ showEvents WRITE setShowEvents NOTIFY showEventsChanged)
Q_PROPERTY(bool showActions READ showActions WRITE setShowActions NOTIFY showActionsChanged)
Q_PROPERTY(bool showStates READ showStates WRITE setShowStates NOTIFY showStatesChanged)
public:
InterfacesProxy(QObject *parent = nullptr);
QStringList shownInterfaces() const { return m_shownInterfaces; }
void setShownInterfaces(const QStringList &shownInterfaces) { m_shownInterfaces = shownInterfaces; emit shownInterfacesChanged(); invalidateFilter(); }
Devices* devicesFilter() const { return m_devicesFilter; }
void setDevicesFilter(Devices *devices) { m_devicesFilter = devices; emit devicesFilterChanged(); invalidateFilter(); }
bool showEvents() const;
void setShowEvents(bool showEvents);
bool showActions() const;
void setShowActions(bool showActions);
bool showStates() const;
void setShowStates(bool showStates);
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
Q_INVOKABLE Interface* get(int index) const;
signals:
void shownInterfacesChanged();
void devicesFilterChanged();
void showEventsChanged();
void showActionsChanged();
void showStatesChanged();
private:
Interfaces *m_interfaces = nullptr;
QStringList m_shownInterfaces;
Devices* m_devicesFilter = nullptr;
bool m_showEvents = false;
bool m_showActions = false;
bool m_showStates = false;
};
#endif // INTERFACESPROXY_H

View File

@ -198,7 +198,10 @@ void RuleManager::parseEventDescriptors(const QVariantList &eventDescriptorList,
eventDescriptor->setInterfaceName(eventDescriptorVariant.toMap().value("interface").toString());
eventDescriptor->setInterfaceEvent(eventDescriptorVariant.toMap().value("interfaceEvent").toString());
foreach (const QVariant &paramDescriptorVariant, eventDescriptorVariant.toMap().value("paramDescriptors").toList()) {
ParamDescriptor *paramDescriptor = new ParamDescriptor(paramDescriptorVariant.toMap().value("paramTypeId").toString(), paramDescriptorVariant.toMap().value("value"));
ParamDescriptor *paramDescriptor = new ParamDescriptor();
paramDescriptor->setParamTypeId(paramDescriptorVariant.toMap().value("paramTypeId").toString());
paramDescriptor->setParamName(paramDescriptorVariant.toMap().value("paramName").toString());
paramDescriptor->setValue(paramDescriptorVariant.toMap().value("value"));
QMetaEnum operatorEnum = QMetaEnum::fromType<ParamDescriptor::ValueOperator>();
paramDescriptor->setOperatorType((ParamDescriptor::ValueOperator)operatorEnum.keyToValue(paramDescriptorVariant.toMap().value("operator").toString().toLocal8Bit()));
eventDescriptor->paramDescriptors()->addParamDescriptor(paramDescriptor);
@ -216,7 +219,14 @@ StateEvaluator *RuleManager::parseStateEvaluator(const QVariantMap &stateEvaluat
QVariantMap sdMap = stateEvaluatorMap.value("stateDescriptor").toMap();
QMetaEnum operatorEnum = QMetaEnum::fromType<StateDescriptor::ValueOperator>();
StateDescriptor::ValueOperator op = (StateDescriptor::ValueOperator)operatorEnum.keyToValue(sdMap.value("operator").toByteArray());
StateDescriptor *sd = new StateDescriptor(sdMap.value("deviceId").toUuid(), op, sdMap.value("stateTypeId").toUuid(), sdMap.value("value"), stateEvaluator);
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);
} else {
sd = new StateDescriptor(sdMap.value("interface").toString(), sdMap.value("interfaceState").toString(), op, sdMap.value("value"), stateEvaluator);
}
qDebug() << "Created StateDescriptor:" << sd->interfaceName() << sd->interfaceState() << sd->deviceId() << sd->stateTypeId();
stateEvaluator->setStateDescriptor(sd);
foreach (const QVariant &childEvaluatorVariant, stateEvaluatorMap.value("childEvaluators").toList()) {
@ -230,39 +240,39 @@ StateEvaluator *RuleManager::parseStateEvaluator(const QVariantMap &stateEvaluat
void RuleManager::parseRuleActions(const QVariantList &ruleActions, Rule *rule)
{
foreach (const QVariant &ruleActionVariant, ruleActions) {
RuleAction *ruleAction = new RuleAction();
ruleAction->setDeviceId(ruleActionVariant.toMap().value("deviceId").toUuid());
ruleAction->setActionTypeId(ruleActionVariant.toMap().value("actionTypeId").toUuid());
foreach (const QVariant &ruleActionParamVariant, ruleActionVariant.toMap().value("ruleActionParams").toList()) {
RuleActionParam *param = new RuleActionParam();
param->setParamTypeId(ruleActionParamVariant.toMap().value("paramTypeId").toUuid());
param->setValue(ruleActionParamVariant.toMap().value("value"));
param->setEventTypeId(ruleActionParamVariant.toMap().value("eventTypeId").toString());
param->setEventParamTypeId(ruleActionParamVariant.toMap().value("eventParamTypeId").toString());
ruleAction->ruleActionParams()->addRuleActionParam(param);
}
rule->actions()->addRuleAction(ruleAction);
rule->actions()->addRuleAction(parseRuleAction(ruleActionVariant.toMap()));
}
}
void RuleManager::parseRuleExitActions(const QVariantList &ruleActions, Rule *rule)
{
foreach (const QVariant &ruleActionVariant, ruleActions) {
RuleAction *ruleAction = new RuleAction();
ruleAction->setDeviceId(ruleActionVariant.toMap().value("deviceId").toUuid());
ruleAction->setActionTypeId(ruleActionVariant.toMap().value("actionTypeId").toUuid());
foreach (const QVariant &ruleActionParamVariant, ruleActionVariant.toMap().value("ruleActionParams").toList()) {
RuleActionParam *param = new RuleActionParam();
param->setParamTypeId(ruleActionParamVariant.toMap().value("paramTypeId").toUuid());
param->setValue(ruleActionParamVariant.toMap().value("value"));
param->setEventTypeId(ruleActionParamVariant.toMap().value("eventTypeId").toString());
param->setEventParamTypeId(ruleActionParamVariant.toMap().value("eventParamTypeId").toString());
ruleAction->ruleActionParams()->addRuleActionParam(param);
}
rule->exitActions()->addRuleAction(ruleAction);
rule->exitActions()->addRuleAction(parseRuleAction(ruleActionVariant.toMap()));
}
}
RuleAction *RuleManager::parseRuleAction(const QVariantMap &ruleAction)
{
RuleAction *ret = new RuleAction();
if (ruleAction.contains("deviceId") && ruleAction.contains("actionTypeId")) {
ret->setDeviceId(ruleAction.value("deviceId").toUuid());
ret->setActionTypeId(ruleAction.value("actionTypeId").toUuid());
} else {
ret->setInterfaceName(ruleAction.value("interface").toString());
ret->setInterfaceAction(ruleAction.value("interfaceAction").toString());
}
foreach (const QVariant &ruleActionParamVariant, ruleAction.value("ruleActionParams").toList()) {
RuleActionParam *param = new RuleActionParam();
param->setParamTypeId(ruleActionParamVariant.toMap().value("paramTypeId").toString());
param->setParamName(ruleActionParamVariant.toMap().value("paramName").toString());
param->setValue(ruleActionParamVariant.toMap().value("value"));
param->setEventTypeId(ruleActionParamVariant.toMap().value("eventTypeId").toString());
param->setEventParamTypeId(ruleActionParamVariant.toMap().value("eventParamTypeId").toString());
ret->ruleActionParams()->addRuleActionParam(param);
}
return ret;
}
void RuleManager::parseTimeDescriptor(const QVariantMap &timeDescriptor, Rule *rule)
{
foreach (const QVariant &timeEventItemVariant, timeDescriptor.value("timeEventItems").toList()) {

View File

@ -8,6 +8,7 @@
class JsonRpcClient;
class StateEvaluator;
class RuleAction;
class RuleManager : public JsonHandler
{
@ -45,6 +46,7 @@ private:
StateEvaluator* parseStateEvaluator(const QVariantMap &stateEvaluatorMap);
void parseRuleActions(const QVariantList &ruleActions, Rule *rule);
void parseRuleExitActions(const QVariantList &ruleActions, Rule *rule);
RuleAction* parseRuleAction(const QVariantMap &ruleAction);
void parseTimeDescriptor(const QVariantMap &timeDescriptor, Rule *rule);
signals:

View File

@ -56,9 +56,6 @@ int ActionTypes::rowCount(const QModelIndex &parent) const
QVariant ActionTypes::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_actionTypes.count())
return QVariant();
ActionType *actionType = m_actionTypes.at(index.row());
switch (role) {
case RoleId:

View File

@ -5,61 +5,39 @@
#include "eventtype.h"
#include "actiontypes.h"
#include "actiontype.h"
#include "statetype.h"
#include "statetypes.h"
#include "device.h"
#include "paramtypes.h"
Interfaces::Interfaces(QObject *parent) : QAbstractListModel(parent)
{
Interface* iface = nullptr;
EventType* et = nullptr;
ActionType* at = nullptr;
ParamType* pt = nullptr;
ParamTypes *pts = nullptr;
iface = new Interface("battery", "Battery powered devices", this);
et = new EventType();
pts = new ParamTypes(et);
et->setParamTypes(pts);
et->setName("batteryLevel");
et->setDisplayName("Battery level changed");
pt = new ParamType("batteryLevel", QVariant::Int, 50);
pt->setDisplayName("Battery Level");
qDebug() << "added param" << pt->type();
pt->setMinValue(0);
pt->setMaxValue(100);
et->paramTypes()->addParamType(pt);
iface->eventTypes()->addEventType(et);
et = new EventType();
pts = new ParamTypes(et);
et->setParamTypes(pts);
et->setName("batteryCritical");
et->setDisplayName("Battery level critical");
pt = new ParamType("batteryCritical", QVariant::Bool, true);
pt->setDisplayName("Battery critical");
et->paramTypes()->addParamType(pt);
iface->eventTypes()->addEventType(et);
m_list.append(iface);
addInterface("battery", tr("Battery powered devices"));
addStateType("battery", "batteryCritical", QVariant::Bool, false,
tr("Battery level is critical"),
tr("Battery level entered critical state"));
iface = new Interface("notification", "Notification services", this);
at = new ActionType();
pts = new ParamTypes(at);
at->setParamTypes(pts);
addInterface("notifications", tr("Notification services"));
pts = createParamTypes("title", tr("Title"), QVariant::String);
addParamType(pts, "body", tr("Message body"), QVariant::String);
addActionType("notifications", "notify", tr("Send notification"), pts);
at->setName("notify");
at->setDisplayName("Send notification");
pt = new ParamType("title", QVariant::String);
pt->setDisplayName("Title");
at->paramTypes()->addParamType(pt);
pt = new ParamType("body", QVariant::String);
pt->setDisplayName("Message body");
at->paramTypes()->addParamType(pt);
iface->actionTypes()->addActionType(at);
m_list.append(iface);
addInterface("light", tr("Lights"));
addStateType("light", "power", QVariant::Bool, true,
tr("Light is turned on"),
tr("A light is turned on or off"),
tr("Turn lights on or off"));
addInterface("temperaturesensor", tr("Temperature sensors"));
addStateType("temperaturesensor", "temperature", QVariant::Double, false,
tr("Temperature"),
tr("Temperature has changed"));
}
int Interfaces::rowCount(const QModelIndex &parent) const
@ -101,3 +79,85 @@ Interface *Interfaces::findByName(const QString &name) const
}
return nullptr;
}
void Interfaces::addInterface(const QString &name, const QString &displayName)
{
Interface *iface = new Interface(name, displayName, this);
m_list.append(iface);
}
void Interfaces::addEventType(const QString &interfaceName, const QString &name, const QString &displayName, ParamTypes *paramTypes)
{
Interface *iface = nullptr;
foreach (Interface* i, m_list) {
if (i->name() == interfaceName) {
iface = i;
break;
}
}
Q_ASSERT_X(iface != nullptr, "Interfaces", "Interface not found");
EventType *et = new EventType();
et->setName(name);
et->setDisplayName(displayName);
et->setParamTypes(paramTypes);
iface->eventTypes()->addEventType(et);
}
void Interfaces::addActionType(const QString &interfaceName, const QString &name, const QString &displayName, ParamTypes *paramTypes)
{
Interface *iface = nullptr;
foreach (Interface* i, m_list) {
if (i->name() == interfaceName) {
iface = i;
break;
}
}
Q_ASSERT_X(iface != nullptr, "Interfaces", "Interface not found");
ActionType *at = new ActionType();
at->setName(name);
at->setDisplayName(displayName);
at->setParamTypes(paramTypes);
iface->actionTypes()->addActionType(at);
}
void Interfaces::addStateType(const QString &interfaceName, const QString &name, QVariant::Type type, bool writable, const QString &displayName, const QString &displayNameEvent, const QString &displayNameAction)
{
Interface *iface = nullptr;
foreach (Interface* i, m_list) {
if (i->name() == interfaceName) {
iface = i;
break;
}
}
Q_ASSERT_X(iface != nullptr, "Interfaces", "Interface not found");
StateType *st = new StateType();
st->setName(name);
st->setDisplayName(displayName);
st->setType(type);
iface->stateTypes()->addStateType(st);
ParamTypes *pts = createParamTypes(name, displayName, type);
addEventType(interfaceName, name, displayNameEvent, pts);
if (writable) {
addActionType(interfaceName, name, displayNameAction, pts);
}
}
ParamTypes *Interfaces::createParamTypes(const QString &name, const QString &displayName, QVariant::Type type, const QVariant &defaultValue, const QVariant &minValue, const QVariant &maxValue)
{
ParamTypes *pts = new ParamTypes();
ParamType *pt = new ParamType(name, type, defaultValue);
pt->setDisplayName(displayName);
pt->setMinValue(minValue);
pt->setMaxValue(maxValue);
pts->addParamType(pt);
return pts;
}
void Interfaces::addParamType(ParamTypes *paramTypes, const QString &name, const QString &displayName, QVariant::Type type, const QVariant &defaultValue, const QVariant &minValue, const QVariant &maxValue)
{
ParamType *pt = new ParamType(name, type, defaultValue);
pt->setDisplayName(displayName);
pt->setMinValue(minValue);
pt->setMaxValue(maxValue);
paramTypes->addParamType(pt);
}

View File

@ -2,8 +2,13 @@
#define INTERFACES_H
#include <QAbstractListModel>
#include <QVariant>
#include <QSortFilterProxyModel>
class Interface;
class ParamType;
class ParamTypes;
class Devices;
class Interfaces : public QAbstractListModel
{
@ -26,6 +31,16 @@ public:
private:
QList<Interface*> m_list;
// helpers to populate the model
void addInterface(const QString &name, const QString &displayName);
void addEventType(const QString &interfaceName, const QString &name, const QString &displayName, ParamTypes *paramTypes);
void addActionType(const QString &interfaceName, const QString &name, const QString &displayName, ParamTypes *paramTypes);
void addStateType(const QString &interfaceName, const QString &name, QVariant::Type type, bool writable, const QString &displayName, const QString &displayNameEvent, const QString &displayNameAction = QString());
ParamTypes* createParamTypes(const QString &name, const QString &displayName, QVariant::Type type, const QVariant &defaultValue = QVariant(), const QVariant &minValue = QVariant(), const QVariant &maxValue = QVariant());
void addParamType(ParamTypes* paramTypes, const QString &name, const QString &displayName, QVariant::Type type, const QVariant &defaultValue = QVariant(), const QVariant &minValue = QVariant(), const QVariant &maxValue = QVariant());
};
#endif // INTERFACES_H

View File

@ -29,6 +29,12 @@ Param::Param(const QString &paramTypeId, const QVariant &value, QObject *parent)
{
}
Param::Param(QObject *parent):
QObject(parent)
{
}
QString Param::paramTypeId() const
{
return m_paramTypeId;

View File

@ -35,6 +35,7 @@ class Param : public QObject
public:
Param(const QString &paramTypeId = QString(), const QVariant &value = QVariant(), QObject *parent = 0);
Param(QObject *parent);
QString paramTypeId() const;
void setParamTypeId(const QString &paramTypeId);
@ -42,14 +43,13 @@ public:
QVariant value() const;
void setValue(const QVariant &value);
private:
QString m_paramTypeId;
QVariant m_value;
signals:
void paramTypeIdChanged();
void valueChanged();
private:
QString m_paramTypeId;
QVariant m_value;
};
#endif // PARAM_H

View File

@ -1,10 +1,23 @@
#include "paramdescriptor.h"
ParamDescriptor::ParamDescriptor(const QString &id, const QVariant &value, QObject *parent) : Param(id, value, parent)
ParamDescriptor::ParamDescriptor(QObject *parent) : Param(parent)
{
}
QString ParamDescriptor::paramName() const
{
return m_paramName;
}
void ParamDescriptor::setParamName(const QString &paramName)
{
if (m_paramName != paramName) {
m_paramName = paramName;
emit paramNameChanged();
}
}
ParamDescriptor::ValueOperator ParamDescriptor::operatorType() const
{
return m_operator;
@ -20,7 +33,10 @@ void ParamDescriptor::setOperatorType(ParamDescriptor::ValueOperator operatorTyp
ParamDescriptor *ParamDescriptor::clone() const
{
ParamDescriptor *ret = new ParamDescriptor(this->paramTypeId(), this->value());
ParamDescriptor *ret = new ParamDescriptor();
ret->setParamTypeId(this->paramTypeId());
ret->setParamName(this->paramName());
ret->setValue(this->value());
ret->setOperatorType(this->operatorType());
return ret;
}

View File

@ -6,6 +6,7 @@
class ParamDescriptor : public Param
{
Q_OBJECT
Q_PROPERTY(QString paramName READ paramName WRITE setParamName NOTIFY paramNameChanged)
Q_PROPERTY(ValueOperator operatorType READ operatorType WRITE setOperatorType NOTIFY operatorTypeChanged)
public:
enum ValueOperator {
@ -18,16 +19,22 @@ public:
};
Q_ENUM(ValueOperator)
explicit ParamDescriptor(const QString &id = QString(), const QVariant &value = QVariant(), QObject *parent = nullptr);
explicit ParamDescriptor(QObject *parent = nullptr);
QString paramName() const;
void setParamName(const QString &paramName);
ValueOperator operatorType() const;
void setOperatorType(ValueOperator operatorType);
ParamDescriptor* clone() const;
signals:
void paramNameChanged();
void operatorTypeChanged();
private:
QString m_paramName;
ValueOperator m_operator;
};

View File

@ -72,3 +72,29 @@ void ParamDescriptors::setParamDescriptor(const QString &paramTypeId, const QVar
paramDescriptor->setOperatorType((ParamDescriptor::ValueOperator)operatorType);
addParamDescriptor(paramDescriptor);
}
void ParamDescriptors::setParamDescriptorByName(const QString &paramName, const QVariant &value, ParamDescriptors::ValueOperator operatorType)
{
foreach (ParamDescriptor* paramDescriptor, m_list) {
if (paramDescriptor->paramName() == paramName) {
paramDescriptor->setValue(value);
paramDescriptor->setOperatorType((ParamDescriptor::ValueOperator)operatorType);
return;
}
}
// Still here? need to add a new one
ParamDescriptor* paramDescriptor = createNewParamDescriptor();
paramDescriptor->setParamName(paramName);
paramDescriptor->setValue(value);
paramDescriptor->setOperatorType((ParamDescriptor::ValueOperator)operatorType);
addParamDescriptor(paramDescriptor);
}
void ParamDescriptors::clear()
{
beginResetModel();
qDeleteAll(m_list);
m_list.clear();
endResetModel();
emit countChanged();
}

View File

@ -39,6 +39,8 @@ public:
void addParamDescriptor(ParamDescriptor* paramDescriptor);
Q_INVOKABLE void setParamDescriptor(const QString &paramTypeId, const QVariant &value, ValueOperator operatorType);
Q_INVOKABLE void setParamDescriptorByName(const QString &paramName, const QVariant &value, ValueOperator operatorType);
Q_INVOKABLE void clear();
signals:
void countChanged();

View File

@ -113,9 +113,9 @@ void Rule::setStateEvaluator(StateEvaluator *stateEvaluator)
emit stateEvaluatorChanged();
}
void Rule::createStateEvaluator()
StateEvaluator* Rule::createStateEvaluator() const
{
setStateEvaluator(new StateEvaluator(this));
return new StateEvaluator();
}
Rule *Rule::clone() const

View File

@ -17,7 +17,7 @@ class Rule : public QObject
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(bool active READ active NOTIFY activeChanged)
Q_PROPERTY(EventDescriptors* eventDescriptors READ eventDescriptors CONSTANT)
Q_PROPERTY(StateEvaluator* stateEvaluator READ stateEvaluator NOTIFY stateEvaluatorChanged)
Q_PROPERTY(StateEvaluator* stateEvaluator READ stateEvaluator WRITE setStateEvaluator NOTIFY stateEvaluatorChanged)
Q_PROPERTY(RuleActions* actions READ actions CONSTANT)
Q_PROPERTY(RuleActions* exitActions READ exitActions CONSTANT)
Q_PROPERTY(TimeDescriptor* timeDescriptor READ timeDescriptor CONSTANT)
@ -42,9 +42,9 @@ public:
RuleActions* exitActions() const;
TimeDescriptor* timeDescriptor() const;
void setStateEvaluator(StateEvaluator* stateEvaluator);
Q_INVOKABLE StateEvaluator* createStateEvaluator() const;
Q_INVOKABLE void createStateEvaluator();
Q_INVOKABLE void setStateEvaluator(StateEvaluator* stateEvaluator);
Q_INVOKABLE Rule *clone() const;

View File

@ -34,6 +34,32 @@ void RuleAction::setActionTypeId(const QUuid &actionTypeId)
}
}
QString RuleAction::interfaceName() const
{
return m_interfaceName;
}
void RuleAction::setInterfaceName(const QString &interfaceName)
{
if (m_interfaceName != interfaceName) {
m_interfaceName = interfaceName;
emit interfaceNameChanged();
}
}
QString RuleAction::interfaceAction() const
{
return m_interfaceAction;
}
void RuleAction::setInterfaceAction(const QString &interfaceAction)
{
if (m_interfaceAction != interfaceAction) {
m_interfaceAction = interfaceAction;
emit interfaceActionChanged();
}
}
RuleActionParams *RuleAction::ruleActionParams() const
{
return m_ruleActionParams;
@ -42,8 +68,10 @@ RuleActionParams *RuleAction::ruleActionParams() const
RuleAction *RuleAction::clone() const
{
RuleAction *ret = new RuleAction();
ret->setActionTypeId(actionTypeId());
ret->setDeviceId(deviceId());
ret->setActionTypeId(actionTypeId());
ret->setInterfaceName(interfaceName());
ret->setInterfaceAction(interfaceAction());
for (int i = 0; i < ruleActionParams()->rowCount(); i++) {
ret->ruleActionParams()->addRuleActionParam(ruleActionParams()->get(i)->clone());
}

View File

@ -11,6 +11,8 @@ class RuleAction : public QObject
Q_OBJECT
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)
Q_PROPERTY(QString interfaceAction READ interfaceAction WRITE setInterfaceAction NOTIFY interfaceActionChanged)
Q_PROPERTY(RuleActionParams* ruleActionParams READ ruleActionParams CONSTANT)
public:
@ -22,6 +24,12 @@ public:
QUuid actionTypeId() const;
void setActionTypeId(const QUuid &actionTypeId);
QString interfaceName() const;
void setInterfaceName(const QString &interfaceName);
QString interfaceAction() const;
void setInterfaceAction(const QString &interfaceAction);
RuleActionParams* ruleActionParams() const;
RuleAction *clone() const;
@ -29,10 +37,14 @@ public:
signals:
void deviceIdChanged();
void actionTypeIdChanged();
void interfaceNameChanged();
void interfaceActionChanged();
private:
QUuid m_deviceId;
QUuid m_actionTypeId;
QString m_interfaceName;
QString m_interfaceAction;
RuleActionParams *m_ruleActionParams;
};

View File

@ -1,33 +1,20 @@
#include "ruleactionparam.h"
RuleActionParam::RuleActionParam(QObject *parent) : QObject(parent)
RuleActionParam::RuleActionParam(QObject *parent) : Param(parent)
{
}
QUuid RuleActionParam::paramTypeId() const
QString RuleActionParam::paramName() const
{
return m_paramTypeId;
return m_paramName;
}
void RuleActionParam::setParamTypeId(const QUuid &paramTypeId)
void RuleActionParam::setParamName(const QString &paramName)
{
if (m_paramTypeId != paramTypeId) {
m_paramTypeId = paramTypeId;
emit paramTypeIdChanged();
}
}
QVariant RuleActionParam::value() const
{
return m_value;
}
void RuleActionParam::setValue(const QVariant &value)
{
if (m_value != value) {
m_value = value;
emit valueChanged();
if (m_paramName != paramName) {
m_paramName = paramName;
emit paramNameChanged();
}
}
@ -61,6 +48,7 @@ RuleActionParam *RuleActionParam::clone() const
{
RuleActionParam *ret = new RuleActionParam();
ret->setParamTypeId(paramTypeId());
ret->setParamName(paramName());
ret->setValue(value());
return ret;
}

View File

@ -5,21 +5,19 @@
#include <QUuid>
#include <QVariant>
class RuleActionParam : public QObject
#include "param.h"
class RuleActionParam : public Param
{
Q_OBJECT
Q_PROPERTY(QUuid paramTypeId READ paramTypeId NOTIFY paramTypeIdChanged)
Q_PROPERTY(QVariant value READ value NOTIFY valueChanged)
Q_PROPERTY(QString paramName READ paramName WRITE setParamName NOTIFY paramNameChanged)
Q_PROPERTY(QString eventTypeId READ eventTypeId WRITE setEventTypeId NOTIFY eventTypeIdChanged)
Q_PROPERTY(QString eventParamTypeId READ eventParamTypeId WRITE setEventParamTypeId NOTIFY eventParamTypeIdChanged)
public:
explicit RuleActionParam(QObject *parent = nullptr);
QUuid paramTypeId() const;
void setParamTypeId(const QUuid &paramTypeId);
QVariant value() const;
void setValue(const QVariant &value);
QString paramName() const;
void setParamName(const QString &paramName);
QString eventTypeId() const;
void setEventTypeId(const QString &eventTypeId);
@ -29,15 +27,12 @@ public:
RuleActionParam* clone() const;
signals:
void paramTypeIdChanged();
void valueChanged();
void paramNameChanged();
void eventTypeIdChanged();
void eventParamTypeIdChanged();
private:
QUuid m_paramTypeId;
QVariant m_value;
QString m_paramName;
QString m_eventTypeId;
QString m_eventParamTypeId;
};

View File

@ -62,6 +62,22 @@ void RuleActionParams::setRuleActionParam(const QString &paramTypeId, const QVar
addRuleActionParam(rap);
}
void RuleActionParams::setRuleActionParamByName(const QString &paramName, const QVariant &value)
{
foreach (RuleActionParam *rap, m_list) {
if (rap->paramName() == paramName) {
rap->setValue(value);
return;
}
}
// Still here? Need to add it
RuleActionParam *rap = new RuleActionParam(this);
rap->setParamName(paramName);
rap->setValue(value);
addRuleActionParam(rap);
}
void RuleActionParams::setRuleActionParamEvent(const QString &paramTypeId, const QString &eventTypeId, const QString &eventParamTypeId)
{
foreach (RuleActionParam *rap, m_list) {

View File

@ -27,6 +27,7 @@ public:
void addRuleActionParam(RuleActionParam* ruleActionParam);
Q_INVOKABLE void setRuleActionParam(const QString &paramTypeId, const QVariant &value);
Q_INVOKABLE void setRuleActionParamByName(const QString &paramName, const QVariant &value);
Q_INVOKABLE void setRuleActionParamEvent(const QString &paramTypeId, const QString &eventTypeId, const QString &eventParamTypeId);
Q_INVOKABLE RuleActionParam* get(int index) const;

View File

@ -1,10 +1,20 @@
#include "statedescriptor.h"
StateDescriptor::StateDescriptor(const QUuid &deviceId, StateDescriptor::ValueOperator valueOperator, const QUuid &stateTypeId, const QVariant &value, QObject *parent):
StateDescriptor::StateDescriptor(const QUuid &deviceId, const QUuid &stateTypeId, StateDescriptor::ValueOperator valueOperator, const QVariant &value, QObject *parent):
QObject(parent),
m_deviceId(deviceId),
m_operator(valueOperator),
m_stateTypeId(stateTypeId),
m_operator(valueOperator),
m_value(value)
{
}
StateDescriptor::StateDescriptor(const QString &interfaceName, const QString &interfaceState, StateDescriptor::ValueOperator valueOperator, const QVariant &value, QObject *parent):
QObject(parent),
m_interfaceName(interfaceName),
m_interfaceState(interfaceState),
m_operator(valueOperator),
m_value(value)
{
@ -54,6 +64,32 @@ void StateDescriptor::setStateTypeId(const QUuid &stateTypeId)
}
}
QString StateDescriptor::interfaceName() const
{
return m_interfaceName;
}
void StateDescriptor::setInterfaceName(const QString &interfaceName)
{
if (m_interfaceName != interfaceName) {
m_interfaceName = interfaceName;
emit interfaceNameChanged();
}
}
QString StateDescriptor::interfaceState() const
{
return m_interfaceState;
}
void StateDescriptor::setInterfaceState(const QString &interfaceState)
{
if (m_interfaceState != interfaceState) {
m_interfaceState = interfaceState;
emit interfaceStateChanged();
}
}
QVariant StateDescriptor::value() const
{
return m_value;
@ -69,6 +105,8 @@ void StateDescriptor::setValue(const QVariant &value)
StateDescriptor *StateDescriptor::clone() const
{
StateDescriptor *ret = new StateDescriptor(deviceId(), valueOperator(), stateTypeId(), value());
StateDescriptor *ret = new StateDescriptor(deviceId(), stateTypeId(), valueOperator(), value());
ret->setInterfaceName(interfaceName());
ret->setInterfaceState(interfaceState());
return ret;
}

View File

@ -9,8 +9,10 @@ class StateDescriptor : public QObject
{
Q_OBJECT
Q_PROPERTY(QUuid deviceId READ deviceId WRITE setDeviceId NOTIFY deviceIdChanged)
Q_PROPERTY(ValueOperator valueOperator READ valueOperator WRITE setValueOperator NOTIFY valueOperatorChanged)
Q_PROPERTY(QUuid stateTypeId READ stateTypeId WRITE setStateTypeId NOTIFY stateTypeIdChanged)
Q_PROPERTY(QString interfaceName READ interfaceName WRITE setInterfaceName NOTIFY interfaceNameChanged)
Q_PROPERTY(QString interfaceState READ interfaceState WRITE setInterfaceState NOTIFY interfaceStateChanged)
Q_PROPERTY(ValueOperator valueOperator READ valueOperator WRITE setValueOperator NOTIFY valueOperatorChanged)
Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged)
public:
@ -24,18 +26,25 @@ public:
};
Q_ENUM(ValueOperator)
explicit StateDescriptor(const QUuid &deviceId, ValueOperator valueOperator, const QUuid &stateTypeId, const QVariant &value, QObject *parent = nullptr);
explicit StateDescriptor(const QUuid &deviceId, const QUuid &stateTypeId, ValueOperator valueOperator, const QVariant &value, QObject *parent = nullptr);
explicit StateDescriptor(const QString &interfaceName, const QString &interfaceState, ValueOperator valueOperator, const QVariant &value, QObject *parent = nullptr);
StateDescriptor(QObject *parent = nullptr);
QUuid deviceId() const;
void setDeviceId(const QUuid &deviceId);
ValueOperator valueOperator() const;
void setValueOperator(ValueOperator valueOperator);
QUuid stateTypeId() const;
void setStateTypeId(const QUuid &stateTypeId);
QString interfaceName() const;
void setInterfaceName(const QString &interfaceName);
QString interfaceState() const;
void setInterfaceState(const QString &interfaceState);
ValueOperator valueOperator() const;
void setValueOperator(ValueOperator valueOperator);
QVariant value() const;
void setValue(const QVariant &value);
@ -43,14 +52,18 @@ public:
signals:
void deviceIdChanged();
void valueOperatorChanged();
void stateTypeIdChanged();
void interfaceNameChanged();
void interfaceStateChanged();
void valueOperatorChanged();
void valueChanged();
private:
QUuid m_deviceId;
ValueOperator m_operator = ValueOperatorEquals;
QUuid m_stateTypeId;
QString m_interfaceName;
QString m_interfaceState;
ValueOperator m_operator = ValueOperatorEquals;
QVariant m_value;
};

View File

@ -66,6 +66,8 @@ StateEvaluator *StateEvaluator::clone() const
ret->m_operator = this->m_operator;
ret->m_stateDescriptor->setDeviceId(this->m_stateDescriptor->deviceId());
ret->m_stateDescriptor->setStateTypeId(this->m_stateDescriptor->stateTypeId());
ret->m_stateDescriptor->setInterfaceName(this->m_stateDescriptor->interfaceName());
ret->m_stateDescriptor->setInterfaceState(this->m_stateDescriptor->interfaceState());
ret->m_stateDescriptor->setValueOperator(this->m_stateDescriptor->valueOperator());
ret->m_stateDescriptor->setValue(this->m_stateDescriptor->value());
for (int i = 0; i < this->m_childEvaluators->rowCount(); i++) {

View File

@ -1,4 +1,5 @@
#include "stateevaluators.h"
#include "stateevaluator.h"
StateEvaluators::StateEvaluators(QObject *parent) : QAbstractListModel(parent)
{
@ -34,13 +35,23 @@ void StateEvaluators::addStateEvaluator(StateEvaluator *stateEvaluator)
StateEvaluator *StateEvaluators::get(int index) const
{
if (index < 0 || index >= m_list.count()) {
return nullptr;
}
return m_list.at(index);
}
StateEvaluator *StateEvaluators::take(int index)
{
beginRemoveRows(QModelIndex(), index, index);
return m_list.takeAt(index);
endInsertRows();
StateEvaluator* ret = m_list.takeAt(index);
endRemoveRows();
emit countChanged();
ret->setParent(nullptr);
return ret;
}
void StateEvaluators::remove(int index)
{
take(index)->deleteLater();
}

View File

@ -19,7 +19,12 @@ public:
void addStateEvaluator(StateEvaluator* stateEvaluator);
Q_INVOKABLE StateEvaluator* get(int index) const;
StateEvaluator* take(int index);
// Caller takes ownership, is responsible for deleting
Q_INVOKABLE StateEvaluator* take(int index);
// StateEvaluator will be deleted
Q_INVOKABLE void remove(int index);
signals:
void countChanged();

View File

@ -27,12 +27,12 @@ StateType::StateType(QObject *parent) :
{
}
QUuid StateType::id() const
QString StateType::id() const
{
return m_id;
}
void StateType::setId(const QUuid &id)
void StateType::setId(const QString &id)
{
m_id = id;
}
@ -67,6 +67,11 @@ void StateType::setType(const QString &type)
m_type = type;
}
void StateType::setType(QVariant::Type type)
{
m_type = QVariant::typeToName(type);
}
int StateType::index() const
{
return m_index;

View File

@ -32,7 +32,7 @@
class StateType : public QObject
{
Q_OBJECT
Q_PROPERTY(QUuid id READ id CONSTANT)
Q_PROPERTY(QString id READ id CONSTANT)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString displayName READ displayName CONSTANT)
Q_PROPERTY(QString type READ type CONSTANT)
@ -44,8 +44,8 @@ class StateType : public QObject
public:
StateType(QObject *parent = 0);
QUuid id() const;
void setId(const QUuid &id);
QString id() const;
void setId(const QString &id);
QString name() const;
void setName(const QString &name);
@ -55,6 +55,7 @@ public:
QString type() const;
void setType(const QString &type);
void setType(QVariant::Type type);
int index() const;
void setIndex(const int &index);
@ -69,7 +70,7 @@ public:
void setUnitString(const QString &unitString);
private:
QUuid m_id;
QString m_id;
QString m_name;
QString m_displayName;
QString m_type;

View File

@ -34,17 +34,13 @@ QList<StateType *> StateTypes::stateTypes()
return m_stateTypes;
}
int StateTypes::count() const
{
return m_stateTypes.count();
}
StateType *StateTypes::get(int index) const
{
qDebug() << "returning" << m_stateTypes.at(index);
return m_stateTypes.at(index);
}
StateType *StateTypes::getStateType(const QUuid &stateTypeId) const
StateType *StateTypes::getStateType(const QString &stateTypeId) const
{
foreach (StateType *stateType, m_stateTypes) {
if (stateType->id() == stateTypeId) {
@ -68,7 +64,7 @@ QVariant StateTypes::data(const QModelIndex &index, int role) const
StateType *stateType = m_stateTypes.at(index.row());
switch (role) {
case RoleId:
return stateType->id().toString();
return stateType->id();
case RoleName:
return stateType->name();
case RoleDisplayName:
@ -87,10 +83,11 @@ QVariant StateTypes::data(const QModelIndex &index, int role) const
void StateTypes::addStateType(StateType *stateType)
{
stateType->setParent(this);
beginInsertRows(QModelIndex(), m_stateTypes.count(), m_stateTypes.count());
//qDebug() << "StateTypes: loaded stateType" << stateType->name();
m_stateTypes.append(stateType);
endInsertRows();
emit countChanged();
}
StateType *StateTypes::findByName(const QString &name) const
@ -110,6 +107,7 @@ void StateTypes::clearModel()
qDeleteAll(m_stateTypes);
m_stateTypes.clear();
endResetModel();
emit countChanged();
}
QHash<int, QByteArray> StateTypes::roleNames() const

View File

@ -31,6 +31,7 @@
class StateTypes : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
public:
enum Role {
@ -47,9 +48,8 @@ public:
QList<StateType *> stateTypes();
Q_INVOKABLE int count() const;
Q_INVOKABLE StateType *get(int index) const;
Q_INVOKABLE StateType *getStateType(const QUuid &stateTypeId) const;
Q_INVOKABLE StateType *getStateType(const QString &stateTypeId) const;
int rowCount(const QModelIndex & parent = QModelIndex()) const;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
@ -63,6 +63,9 @@ public:
protected:
QHash<int, QByteArray> roleNames() const;
signals:
void countChanged();
private:
QList<StateType *> m_stateTypes;

View File

@ -198,5 +198,12 @@
<file>ui/images/action.svg</file>
<file>ui/images/event.svg</file>
<file>ui/images/state.svg</file>
<file>ui/images/event-interface.svg</file>
<file>ui/components/MeaListItemDelegate.qml</file>
<file>ui/images/state-interface.svg</file>
<file>ui/images/action-interface.svg</file>
<file>ui/system/AboutNymeaPage.qml</file>
<file>ui/images/logs.svg</file>
<file>ui/images/plugin.svg</file>
</qresource>
</RCC>

View File

@ -110,24 +110,11 @@ Page {
}
}
ThinDivider {}
ItemDelegate {
MeaListItemDelegate {
Layout.fillWidth: true
contentItem: RowLayout {
Label {
Layout.fillWidth: true
text: qsTr("About %1").arg(app.appName)
}
Image {
source: "images/next.svg"
Layout.preferredHeight: parent.height
Layout.preferredWidth: height
}
}
onClicked: {
pageStack.push(Qt.resolvedUrl("AboutPage.qml"))
}
text: qsTr("About %1").arg(app.appName)
iconName: "../images/info.svg"
onClicked: pageStack.push(Qt.resolvedUrl("AboutPage.qml"))
}
}

View File

@ -61,24 +61,15 @@ Page {
anchors.fill: parent
model: Engine.ruleManager.rules
delegate: SwipeDelegate {
delegate: MeaListItemDelegate {
id: ruleDelegate
width: parent.width
iconName: "../images/magic.svg"
iconColor: !model.enabled ? "red" : (model.active ? app.guhAccent : "grey")
text: model.name
canDelete: true
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
height: app.iconSize
width: height
name: "../images/magic.svg"
color: !model.enabled ? "red" : (model.active ? app.guhAccent : "grey")
}
Label {
Layout.fillWidth: true
text: model.name
}
}
onDeleteClicked: Engine.ruleManager.removeRule(model.id)
onClicked: {
d.editRulePage = pageStack.push(Qt.resolvedUrl("magic/EditRulePage.qml"), {rule: Engine.ruleManager.rules.get(index).clone()})
@ -88,25 +79,12 @@ Page {
})
d.editRulePage.onAccept.connect(function() {
d.editRulePage.busy = true;
Engine.ruleManager.editRule(editRulePage.rule);
Engine.ruleManager.editRule(d.editRulePage.rule);
})
d.editRulePage.onCancel.connect(function() {
pageStack.pop();
})
}
swipe.right: MouseArea {
height: ruleDelegate.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: Engine.ruleManager.removeRule(model.id)
}
}
}

View File

@ -16,7 +16,7 @@ ApplicationWindow {
property int margins: 14
property int bigMargins: 20
property int smallFont: 14
property int smallFont: 13
property int mediumFont: 16
property int largeFont: 20
property int iconSize: 30
@ -220,6 +220,8 @@ ApplicationWindow {
return Qt.resolvedUrl("images/sort-listitem.svg")
case "garagegate":
return Qt.resolvedUrl("images/shutter-10.svg")
case "battery":
return Qt.resolvedUrl("images/battery/battery-050.svg")
}
return "";
}

View File

@ -89,41 +89,25 @@ Page {
}
ItemDelegate {
MeaListItemDelegate {
Layout.fillWidth: true
contentItem: RowLayout {
Label {
Layout.fillWidth: true
text: qsTr("Plugins")
}
Image {
source: "images/next.svg"
Layout.preferredHeight: parent.height
Layout.preferredWidth: height
}
}
onClicked: {
pageStack.push(Qt.resolvedUrl("system/PluginsPage.qml"))
}
iconName: "../images/plugin.svg"
text: qsTr("Plugins")
onClicked:pageStack.push(Qt.resolvedUrl("system/PluginsPage.qml"))
}
ItemDelegate {
MeaListItemDelegate {
Layout.fillWidth: true
contentItem: RowLayout {
Label {
text: qsTr("Log viewer")
Layout.fillWidth: true
}
Image {
source: "images/next.svg"
Layout.preferredHeight: parent.height
Layout.preferredWidth: height
}
}
iconName: "../images/logs.svg"
text: qsTr("Log viewer")
onClicked: pageStack.push(Qt.resolvedUrl("system/LogViewerPage.qml"))
}
MeaListItemDelegate {
Layout.fillWidth: true
iconName: "../images/info.svg"
text: qsTr("About nymea")
onClicked: pageStack.push(Qt.resolvedUrl("system/AboutNymeaPage.qml"))
}
}
Component {

View File

@ -10,7 +10,7 @@ ToolBar {
property string text
property alias backButtonVisible: backButton.visible
property alias menuButtonVisible: menuButton.visible
default property alias data: layout.data
default property alias children: layout.data
signal backPressed();
signal menuPressed();

View File

@ -0,0 +1,79 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
SwipeDelegate {
id: root
property string subText
property bool progressive: true
property bool canDelete: false
property string iconName
property int iconSize: app.iconSize
property color iconColor: app.guhAccent
signal deleteClicked()
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
id: icon
Layout.preferredHeight: root.iconSize
Layout.preferredWidth: height
name: root.iconName
color: root.iconColor
visible: root.iconName
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: root.text
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: root.subText
font.pixelSize: app.smallFont
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
visible: root.subText.length > 0
}
}
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/next.svg"
visible: root.progressive
}
}
swipe.enabled: canDelete
swipe.right: MouseArea {
height: root.height
width: height
anchors.right: parent.right
Rectangle {
anchors.fill: parent
color: "red"
}
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "white"
}
onClicked: {
swipe.close();
root.deleteClicked()
}
}
}

View File

@ -3,30 +3,13 @@ import QtQuick.Controls 2.2
import QtQuick.Layouts 1.2
import "../components"
ItemDelegate {
MeaListItemDelegate {
id: root
width: parent.width
iconName: app.interfacesToIcon(root.interfaces)
text: root.name
progressive: true
property var interfaces: []
property var name: ""
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
height: app.iconSize
width: height
name: app.interfacesToIcon(root.interfaces)
color: app.guhAccent
}
Label {
Layout.fillWidth: true
text: root.name
}
Image {
source: "../images/next.svg"
Layout.preferredHeight: parent.height
Layout.preferredWidth: height
}
}
property string name: ""
}

View File

@ -78,11 +78,12 @@ Page {
onAddRuleClicked: {
var rule = Engine.ruleManager.createNewRule();
rule.createStateEvaluator();
rule.stateEvaluator.stateDescriptor.deviceId = device.id;
rule.stateEvaluator.stateDescriptor.stateTypeId = root.stateType.id;
rule.stateEvaluator.stateDescriptor.value = value;
rule.stateEvaluator.stateDescriptor.valueOperator = StateDescriptor.ValueOperatorEquals;
var stateEvaluator = rule.createStateEvaluator();
stateEvaluator.stateDescriptor.deviceId = device.id;
stateEvaluator.stateDescriptor.stateTypeId = root.stateType.id;
stateEvaluator.stateDescriptor.value = value;
stateEvaluator.stateDescriptor.valueOperator = StateDescriptor.ValueOperatorEquals;
rule.setStateEvaluator(stateEvaluator);
rule.name = root.device.name + " - " + stateType.displayName + " = " + value;
var rulePage = pageStack.push(Qt.resolvedUrl("../magic/DeviceRulesPage.qml"), {device: root.device});

View File

@ -0,0 +1,209 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
id="svg4874"
version="1.1"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
viewBox="0 0 96 96.000001"
sodipodi:docname="action-interface.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="14.049998"
inkscape:cx="69.993806"
inkscape:cy="44.483695"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="true"
inkscape:window-width="2880"
inkscape:window-height="1698"
inkscape:window-x="0"
inkscape:window-y="44"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="84,-8.0000001"
id="guide4080"
inkscape:locked="false" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170"
inkscape:locked="false" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172"
inkscape:locked="false" />
<sodipodi:guide
position="92,-8.0000001"
orientation="1,0"
id="guide4760"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-78.50504)">
<rect
transform="rotate(90)"
y="-28.142855"
x="78.505043"
height="96"
width="96"
id="rect4782-637"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:3.99999976;marker:none;enable-background:accumulate" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="rect4747-4"
d="m 18.142883,124.50503 1.000025,3.99998 h -32.999999 v -3.99998 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;stroke:none;stroke-width:8.99999905;marker:none;enable-background:accumulate"
inkscape:transform-center-x="-26.500014" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 5.6230724,101.05661 C -4.6712646,90.767855 -20.167327,87.685805 -33.61715,93.253885 c -13.44979,5.56807 -22.23047,18.700765 -22.23047,33.251945 0,14.55118 8.78068,27.68194 22.23047,33.25002 13.449823,5.56804 28.9458854,2.48602 39.2402224,-7.80276 l -2.828107,-2.82811 c -9.158173,9.15322 -22.9166364,11.88745 -34.8828054,6.93358 -11.96618,-4.95383 -19.75979,-16.60879 -19.75979,-29.55273 0,-12.94393 7.79361,-24.60083 19.75979,-29.554705 11.966169,-4.95382 25.7246324,-2.2196 34.8828054,6.933635 z"
id="path4145"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path5837"
d="m 9.5430153,119.30443 0.00456,14.4 c 2.1900247,-1.00124 4.4195527,-2.12156 6.6895137,-3.35923 2.248645,-1.24067 4.417693,-2.5203 6.505716,-3.84016 -2.088023,-1.29345 -4.257071,-2.56083 -6.505716,-3.8015 -2.271209,-1.23836 -4.502007,-2.37113 -6.6931875,-3.39911 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.79964399;marker:none;enable-background:accumulate" />
<rect
transform="scale(1,-1)"
y="-174.50505"
x="-67.857147"
height="96"
width="96.000008"
id="rect4782-0"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:3.99920893;marker:none;enable-background:accumulate" />
<g
id="g1546">
<path
sodipodi:nodetypes="sccccccsccs"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -14.85739,128.50594 c 8.51248,0 14.61615,-1.67112 18.89488,-4.54103 4.27876,-2.86991 6.59021,-6.93883 7.55365,-11.13672 0.481728,-2.09895 0.627128,-6.33371 0.627128,-6.33371 l -4.0339524,0.0815 c 0,0 -0.096796,3.64414 -0.4900656,5.35769 -0.78655,3.42713 -2.47562,6.42067 -5.88438,8.70705 -3.40876,2.28635 -8.67972,3.86521 -16.66726,3.86521 H -47.858 v 4.00003 z"
id="path4116"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path1542"
d="m 1.9230841,111.7131 14.1821019,2.49562 c -0.605737,-2.33062 -1.321885,-4.72081 -2.146577,-7.17122 -0.831349,-2.42991 -1.714887,-4.78821 -2.652114,-7.073694 -1.6363806,1.831694 -3.2611572,3.747714 -4.8734519,5.746744 -1.6139397,2.02168 -3.1168728,4.02187 -4.50973,6.00126 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.79964399;marker:none;enable-background:accumulate" />
</g>
<g
id="g1552"
transform="matrix(1,0,0,-1,8.54e-4,253.01099)">
<path
inkscape:connector-curvature="0"
id="path1548"
d="m -14.85739,128.50594 c 8.51248,0 14.61615,-1.67112 18.89488,-4.54103 4.27876,-2.86991 6.59021,-6.93883 7.55365,-11.13672 0.481728,-2.09895 0.627128,-6.33371 0.627128,-6.33371 l -4.0339524,0.0815 c 0,0 -0.096796,3.64414 -0.4900656,5.35769 -0.78655,3.42713 -2.47562,6.42067 -5.88438,8.70705 -3.40876,2.28635 -8.67972,3.86521 -16.66726,3.86521 H -47.858 v 4.00003 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
sodipodi:nodetypes="sccccccsccs" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.79964399;marker:none;enable-background:accumulate"
d="m 1.9230841,111.7131 14.1821019,2.49562 c -0.605737,-2.33062 -1.321885,-4.72081 -2.146577,-7.17122 -0.831349,-2.42991 -1.714887,-4.78821 -2.652114,-7.073694 -1.6363806,1.831694 -3.2611572,3.747714 -4.8734519,5.746744 -1.6139397,2.02168 -3.1168728,4.02187 -4.50973,6.00126 z"
id="path1550"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="96"
height="96"
id="svg4874"
version="1.1"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
viewBox="0 0 96 96.000001"
sodipodi:docname="event-interface.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="7.024999"
inkscape:cx="70.049325"
inkscape:cy="25.941564"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="true"
inkscape:window-width="2880"
inkscape:window-height="1698"
inkscape:window-x="0"
inkscape:window-y="44"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077"
inkscape:locked="false" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076"
inkscape:locked="false" />
<sodipodi:guide
orientation="1,0"
position="84,-8.0000001"
id="guide4080"
inkscape:locked="false" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170"
inkscape:locked="false" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172"
inkscape:locked="false" />
<sodipodi:guide
position="92,-8.0000001"
orientation="1,0"
id="guide4760"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-78.50504)">
<rect
transform="rotate(90)"
y="-28.142855"
x="78.505043"
height="96"
width="96"
id="rect4782-637"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:3.99999976;marker:none;enable-background:accumulate" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -45.337483,101.05663 c 10.294337,-10.288755 25.790399,-13.370805 39.240222,-7.802725 13.44979,5.56807 22.23047,18.700765 22.23047,33.251945 0,14.55118 -8.78068,27.68194 -22.23047,33.25002 -13.449823,5.56804 -28.945885,2.48602 -39.240222,-7.80276 l 2.828107,-2.82811 c 9.158173,9.15322 22.916636,11.88745 34.882805,6.93358 11.96618,-4.95383 19.75979,-16.60879 19.75979,-29.55273 0,-12.94393 -7.79361,-24.60083 -19.75979,-29.554705 -11.966169,-4.95382 -25.724632,-2.2196 -34.882805,6.933635 z"
id="path4145"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4116"
d="m -28.791428,128.51885 c -8.51248,0 -14.61615,-1.67112 -18.89488,-4.54103 -4.27876,-2.86991 -6.59021,-6.93884 -7.55365,-11.13672 -0.481727,-2.09895 -0.627128,-6.33371 -0.627128,-6.33371 l 4.033952,0.0815 c 0,0 0.0968,3.64414 0.490066,5.35769 0.78655,3.42713 2.47562,6.42066 5.88438,8.70705 3.40876,2.28635 8.67972,3.86521 16.66726,3.86521 h 5.00061 v 4.00003 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
sodipodi:nodetypes="sccccccsccs" />
<path
inkscape:connector-curvature="0"
id="path4118"
d="m -55.867086,146.58106 c 0,0 0.145401,-4.23476 0.627128,-6.3337 0.96344,-4.19788 3.27489,-8.26488 7.55365,-11.13479 4.27873,-2.86987 10.3824,-4.54295 18.89488,-4.54295 h 6.00062 v 3.99998 h -6.00062 c -7.98754,0 -13.2585,1.5789 -16.66726,3.86525 -3.40876,2.28635 -5.09783,5.27993 -5.88438,8.70701 -0.39327,1.71356 -0.490066,5.35772 -0.490066,5.35772 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
sodipodi:nodetypes="cccsccssccc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.99940658;marker:none;enable-background:accumulate"
d="m -31.791338,114.51796 0.008,24 c 3.65004,-1.66877 7.365923,-3.53593 11.149193,-5.59872 3.74774,-2.06778 7.36282,-4.20053 10.84286,-6.40029 -3.48004,-2.15573 -7.09512,-4.26804 -10.84286,-6.33581 -3.78535,-2.06393 -7.503353,-3.95192 -11.155363,-5.66518 z"
id="path5588-9-2-96-5"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.9 KiB

177
mea/ui/images/logs.svg Normal file
View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="90"
height="90"
id="svg4874"
version="1.1"
inkscape:version="0.48+devel r"
viewBox="0 0 90 90.000001"
sodipodi:docname="text-x-generic-symbolic.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.259629"
inkscape:cx="5.0926006"
inkscape:cy="70.49882"
inkscape:document-units="px"
inkscape:current-layer="g5283"
showgrid="true"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="6" />
<sodipodi:guide
orientation="1,0"
position="6,77"
id="guide4063" />
<sodipodi:guide
orientation="1,0"
position="3,78"
id="guide4065" />
<sodipodi:guide
orientation="0,1"
position="55,84"
id="guide4067" />
<sodipodi:guide
orientation="0,1"
position="53,87"
id="guide4069" />
<sodipodi:guide
orientation="0,1"
position="20,3"
id="guide4071" />
<sodipodi:guide
orientation="0,1"
position="20,6"
id="guide4073" />
<sodipodi:guide
orientation="1,0"
position="87,7"
id="guide4075" />
<sodipodi:guide
orientation="1,0"
position="84,7"
id="guide4077" />
<sodipodi:guide
orientation="0,1"
position="58,81"
id="guide4074" />
<sodipodi:guide
orientation="1,0"
position="9,74"
id="guide4076" />
<sodipodi:guide
orientation="0,1"
position="21,9"
id="guide4078" />
<sodipodi:guide
orientation="1,0"
position="81,4"
id="guide4080" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-84.50504)">
<g
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
id="g4845"
style="display:inline">
<g
id="g5283"
transform="matrix(0,-1,-1,0,-293.63782,2219.3622)">
<rect
y="-725.63782"
x="1778"
height="90"
width="90"
id="rect5285"
style="fill:none;stroke:none" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
d="m 9,3 0,84 51,0 3,0 18,-18 0,-3 0,-63 z m 6,6 60,0 0,57 -15,0 0,15 -45,0 z"
transform="translate(1778,-725.63782)"
id="path5289"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccccccc" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="rect5293"
d="m 1802,-707.63781 42,0 0,3.99999 -42,0 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;enable-background:accumulate" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;enable-background:accumulate"
d="m 1802,-699.63781 42,0 0,3.99999 -42,0 z"
id="path4615"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path4617"
d="m 1802,-691.63781 42,0 0,3.99999 -42,0 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;enable-background:accumulate" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;enable-background:accumulate"
d="m 1802,-683.63781 42,0 0,3.99999 -42,0 z"
id="path4619"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path4621"
d="m 1802,-675.63781 21,0 0,3.99999 -21,0 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;marker:none;enable-background:accumulate" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

164
mea/ui/images/plugin.svg Normal file
View File

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="90"
height="90"
id="svg4874"
version="1.1"
inkscape:version="0.48+devel r"
viewBox="0 0 90 90.000001"
sodipodi:docname="package-x-generic-symbolic.svg">
<defs
id="defs4876" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4.0745362"
inkscape:cx="-10.320196"
inkscape:cy="41.857524"
inkscape:document-units="px"
inkscape:current-layer="g5283"
showgrid="false"
showborder="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:object-nodes="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
showguides="true"
inkscape:guide-bbox="true">
<inkscape:grid
type="xygrid"
id="grid5451"
empspacing="6" />
<sodipodi:guide
orientation="1,0"
position="6,77"
id="guide4063" />
<sodipodi:guide
orientation="1,0"
position="3,78"
id="guide4065" />
<sodipodi:guide
orientation="0,1"
position="55,84"
id="guide4067" />
<sodipodi:guide
orientation="0,1"
position="53,87"
id="guide4069" />
<sodipodi:guide
orientation="0,1"
position="20,3"
id="guide4071" />
<sodipodi:guide
orientation="0,1"
position="20,6"
id="guide4073" />
<sodipodi:guide
orientation="1,0"
position="87,7"
id="guide4075" />
<sodipodi:guide
orientation="1,0"
position="84,7"
id="guide4077" />
<sodipodi:guide
orientation="0,1"
position="58,81"
id="guide4074" />
<sodipodi:guide
orientation="1,0"
position="9,74"
id="guide4076" />
<sodipodi:guide
orientation="0,1"
position="21,9"
id="guide4078" />
<sodipodi:guide
orientation="1,0"
position="81,4"
id="guide4080" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(67.857146,-84.50504)">
<g
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
id="g4845"
style="display:inline">
<g
id="g5283"
transform="matrix(0,-1,-1,0,-293.63782,2219.3622)">
<rect
y="-725.63782"
x="1778"
height="90"
width="90"
id="rect5285"
style="fill:none;stroke:none" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#808080;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 1823,-639.00386 -38.8426,-19.35382 0,-41.47248 38.8426,19.35383 38.8426,-19.35383 0,41.47248 z"
id="path4170"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path4174"
d="m 1861.8426,-664.67723 0,-38.2407 -38.8426,-19.35382 -38.8426,19.35382 0,38.2407"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#808080;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#808080;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
d="m 1823,-680.6378 0,41.63394"
id="path4176"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:#808080;stroke-width:2.00000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 1836.3594,-712.26282 -38.1485,19.01563 0,22.12304 6.4668,3.22461 0,-21.34765 38.9297,-19.4043 z"
id="path4178"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg4874"
width="96"
height="96"
version="1.1"
viewBox="0 0 96 96"
sodipodi:docname="state-interface.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<defs
id="defs1598" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2880"
inkscape:window-height="1698"
id="namedview1596"
showgrid="true"
inkscape:zoom="13.906433"
inkscape:cx="42.524831"
inkscape:cy="54.157101"
inkscape:window-x="0"
inkscape:window-y="44"
inkscape:window-maximized="1"
inkscape:current-layer="svg4874">
<inkscape:grid
type="xygrid"
id="grid2159" />
</sodipodi:namedview>
<metadata
id="metadata4879">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<rect
style="color:#000000;fill:none"
height="96"
width="96"
y="-96"
x="-2.7465819e-06"
transform="rotate(90)"
id="rect4782-2" />
<path
inkscape:connector-curvature="0"
style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#808080;color-rendering:auto;image-rendering:auto;shape-rendering:auto"
d="m 27.988,19.999 -0.01134,0.002 c -5.0328,0.0582 -8.7136,-0.12019 -11.725,1.541 -1.5055,0.83062 -2.6968,2.2356 -3.3555,3.9902 -0.65866,1.7546 -0.89647,3.8364 -0.89647,6.4668 v 48.002 c 0,2.6304 0.23773,4.7122 0.89647,6.4668 0.65866,1.7546 1.85,3.1596 3.3555,3.9902 3.011,1.6613 6.6918,1.4848 11.725,1.543 h 0.01134 40.023 0.01134 c 5.0328,-0.0582 8.7136,0.1183 11.725,-1.543 1.5055,-0.83066 2.6968,-2.2356 3.3555,-3.9902 0.65866,-1.7546 0.8965,-3.8364 0.8965,-6.4668 V 31.999 c 0,-2.6304 -0.23773,-4.7122 -0.8965,-6.4668 -0.65866,-1.7547 -1.85,-3.1596 -3.3555,-3.9902 -3.011,-1.6613 -6.6918,-1.4829 -11.725,-1.5411 l -0.01134,-0.002 -12.012,0.002 v 4 l 11.977,-0.002 c 5.0542,0.0586 8.3726,0.23547 9.8398,1.0449 0.73364,0.40479 1.1527,0.85493 1.543,1.8945 0.39024,1.0396 0.64059,2.691 0.64059,5.0606 v 48.002 c 0,2.3695 -0.2502,4.0209 -0.64059,5.0605 -0.39027,1.0396 -0.80935,1.4898 -1.543,1.8945 -1.4645,0.80806 -4.7782,0.98615 -9.8164,1.0449 h -39.977 -0.02268 c -5.0383,-0.059 -8.3519,-0.23697 -9.8164,-1.0449 -0.73364,-0.40475 -1.1508,-0.85489 -1.541,-1.8945 -0.39027,-1.0396 -0.6426,-2.691 -0.6426,-5.0605 v -48.002 c 0,-2.3696 0.25247,-4.0209 0.6426,-5.0606 0.39024,-1.0396 0.80734,-1.4897 1.541,-1.8945 1.4645,-0.80807 4.7782,-0.98616 9.8164,-1.0449 l 12,0.002 v -4 z"
id="path4643-2" />
<path
inkscape:connector-curvature="0"
style="color:#000000;fill:#808080"
d="m 59.9999,46.005 -24,0.008 c 1.6687,3.6501 3.5359,7.366 5.5987,11.149 2.0678,3.7477 4.2005,7.3628 6.4003,10.843 2.1557,-3.48 4.268,-7.0951 6.3358,-10.843 2.0639,-3.7854 3.9519,-7.5033 5.6652,-11.155 z"
id="path5588-9-2-96-04" />
<path
inkscape:connector-curvature="0"
id="path4116"
d="m 50.000025,30.99939 c 0,-8.51248 -1.671118,-14.61615 -4.541027,-18.89488 C 42.589089,7.82575 38.520163,5.5143 34.32228,4.55086 25.926475,2.62395 16.909393,5.58849 12.367195,7.10257 l 1.265612,3.79344 C 18.090646,9.41005 26.573531,6.87467 33.427741,8.44775 36.854866,9.2343 39.848403,10.92337 42.13479,14.33213 44.42114,17.74089 46,23.01185 46,30.99939 V 64 h 4.000025 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
inkscape:connector-curvature="0"
id="path4118"
d="M 83.683627,7.10257 C 79.141429,5.58849 70.124308,2.62395 61.728541,4.55086 57.530658,5.5143 53.463659,7.82575 50.593751,12.10451 47.72388,16.38324 46.050796,22.48691 46.050796,30.99939 v 20.00062 h 3.999988 V 30.99939 c 0,-7.98754 1.578897,-13.2585 3.865247,-16.66726 2.28635,-3.40876 5.279924,-5.09783 8.707011,-5.88438 6.854249,-1.57308 15.337133,0.9623 19.794935,2.44826 z"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:none;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.99999976;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
sodipodi:nodetypes="cccsccssccc" />
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.99940658;marker:none;enable-background:accumulate"
d="m 35.999143,45.99948 24,0.008 c -1.668775,3.65004 -3.535937,7.365923 -5.598728,11.149193 -2.067779,3.74774 -4.200529,7.36282 -6.400289,10.84286 -2.15573,-3.48004 -4.268032,-7.09512 -6.335811,-10.84286 C 39.60039,53.371323 37.712403,49.65332 35.999143,46.00131 Z"
id="path5588-9-2-96-5"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -4,9 +4,11 @@ import QtQuick.Layouts 1.3
import Mea 1.0
import "../components"
SwipeDelegate {
MeaListItemDelegate {
id: root
implicitHeight: app.delegateHeight
progressive: false
canDelete: true
property var calendarItem: null
@ -15,90 +17,7 @@ SwipeDelegate {
signal removeCalendarItem();
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: "../images/clock-app-symbolic.svg"
color: app.guhAccent
}
ColumnLayout {
Label {
Layout.fillWidth: true
elide: Text.ElideRight
text: qsTr("From %1 to %2")
.arg(root.isDateBased ? Qt.formatDateTime(root.calendarItem.dateTime) : Qt.formatTime(root.calendarItem.startTime))
.arg(root.isDateBased ? Qt.formatDateTime(new Date(root.calendarItem.dateTime.getTime() + root.calendarItem.duration * 60000)) : Qt.formatTime(new Date(root.calendarItem.startTime.getTime() + root.calendarItem.duration * 60000)))
}
Label {
Layout.fillWidth: true
elide: Text.ElideRight
font.pixelSize: app.smallFont
text: qsTr("repeated %3")
.arg(repeatingString)
property string repeatingString: {
switch (root.calendarItem.repeatingOption.repeatingMode) {
case RepeatingOption.RepeatingModeNone:
return qsTr("never");
case RepeatingOption.RepeatingModeHourly:
return qsTr("hourly");
case RepeatingOption.RepeatingModeDaily:
return qsTr("daily");
case RepeatingOption.RepeatingModeWeekly:
var weekdays = []
for (var i = 0; i < root.calendarItem.repeatingOption.weekDays.length; i++) {
switch (root.calendarItem.repeatingOption.weekDays[i]) {
case 1:
weekdays.push(qsTr("Mon"));
break;
case 2:
weekdays.push(qsTr("Tue"));
break;
case 3:
weekdays.push(qsTr("Wed"));
break;
case 4:
weekdays.push(qsTr("Thu"));
break;
case 5:
weekdays.push(qsTr("Fri"));
break;
case 6:
weekdays.push(qsTr("Sat"));
break;
case 7:
weekdays.push(qsTr("Sun"));
break;
}
}
return qsTr("weekly on %1").arg(weekdays.join(', '));
case RepeatingOption.RepeatingModeMonthly:
return qsTr("monthly on the %1").arg(root.calendarItem.repeatingOption.monthDays.join(', '));
case RepeatingOption.RepeatingModeYearly:
return qsTr("every year");
}
}
}
}
}
swipe.right: MouseArea {
height: root.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.removeCalendarItem()
}
onDeleteClicked: root.removeCalendarItem()
onClicked: {
var page = pageStack.push(Qt.resolvedUrl("EditCalendarItemPage.qml"), {calendarItem: root.calendarItem})
@ -108,4 +27,59 @@ SwipeDelegate {
print("calendarItem.time is now", root.calendarItem.time)
})
}
iconName: "../images/clock-app-symbolic.svg"
text: qsTr("From %1 to %2")
.arg(root.isDateBased ? Qt.formatDateTime(root.calendarItem.dateTime) : Qt.formatTime(root.calendarItem.startTime))
.arg(root.isDateBased ? Qt.formatDateTime(new Date(root.calendarItem.dateTime.getTime() + root.calendarItem.duration * 60000)) : Qt.formatTime(new Date(root.calendarItem.startTime.getTime() + root.calendarItem.duration * 60000)))
subText: qsTr("repeated %3")
.arg(repeatingString)
property string repeatingString: {
switch (root.calendarItem.repeatingOption.repeatingMode) {
case RepeatingOption.RepeatingModeNone:
return qsTr("never");
case RepeatingOption.RepeatingModeHourly:
return qsTr("hourly");
case RepeatingOption.RepeatingModeDaily:
return qsTr("daily");
case RepeatingOption.RepeatingModeWeekly:
var weekdays = []
for (var i = 0; i < root.calendarItem.repeatingOption.weekDays.length; i++) {
switch (root.calendarItem.repeatingOption.weekDays[i]) {
case 1:
weekdays.push(qsTr("Mon"));
break;
case 2:
weekdays.push(qsTr("Tue"));
break;
case 3:
weekdays.push(qsTr("Wed"));
break;
case 4:
weekdays.push(qsTr("Thu"));
break;
case 5:
weekdays.push(qsTr("Fri"));
break;
case 6:
weekdays.push(qsTr("Sat"));
break;
case 7:
weekdays.push(qsTr("Sun"));
break;
}
}
return qsTr("weekly on %1").arg(weekdays.join(', '));
case RepeatingOption.RepeatingModeMonthly:
return qsTr("monthly on the %1").arg(root.calendarItem.repeatingOption.monthDays.join(', '));
case RepeatingOption.RepeatingModeYearly:
return qsTr("every year");
}
}
}

View File

@ -20,23 +20,39 @@ Page {
signal accept();
signal cancel();
function addEventDescriptor() {
var eventDescriptor = root.rule.eventDescriptors.createNewEventDescriptor();
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"));
page.onBackPressed.connect(function() { pageStack.pop(); });
function addEventDescriptor(interfaceMode) {
if (interfaceMode === undefined) {
interfaceMode = false;
}
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {selectInterface: interfaceMode, showEvents: true});
page.onBackPressed.connect(function() {
pageStack.pop();
});
page.onThingSelected.connect(function(device) {
var eventDescriptor = root.rule.eventDescriptors.createNewEventDescriptor();
eventDescriptor.deviceId = device.id;
selectEventDescriptorData(eventDescriptor)
selectEventDescriptorData(eventDescriptor);
})
page.onInterfaceSelected.connect(function(interfaceName) {
var eventDescriptor = root.rule.eventDescriptors.createNewEventDescriptor();
eventDescriptor.interfaceName = interfaceName;
selectEventDescriptorData(eventDescriptor)
selectEventDescriptorData(eventDescriptor);
})
}
function addInterfaceEventDescriptor() {
addEventDescriptor(true);
}
function selectEventDescriptorData(eventDescriptor) {
var eventPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorPage.qml"), {text: "Select event", eventDescriptor: eventDescriptor});
eventPage.onBackPressed.connect(function() {pageStack.pop()})
eventPage.onBackPressed.connect(function() {
eventPage.StackView.onRemoved.connect(function() {
eventDescriptor.destroy();
});
pageStack.pop();
})
eventPage.onDone.connect(function() {
root.rule.eventDescriptors.addEventDescriptor(eventPage.eventDescriptor);
pageStack.pop(root)
@ -47,8 +63,10 @@ Page {
var timeEventItem = root.rule.timeDescriptor.timeEventItems.createNewTimeEventItem();
var page = pageStack.push(Qt.resolvedUrl("EditTimeEventItemPage.qml"), {timeEventItem: timeEventItem});
page.onBackPressed.connect(function() {
page.StackView.onRemoved.connect(function() {
timeEventItem.destroy();
});
pageStack.pop()
timeEventItem.destroy();
})
page.onDone.connect(function() {
root.rule.timeDescriptor.timeEventItems.addTimeEventItem(timeEventItem);
@ -60,8 +78,10 @@ Page {
var calendarItem = root.rule.timeDescriptor.calendarItems.createNewCalendarItem();
var page = pageStack.push(Qt.resolvedUrl("EditCalendarItemPage.qml"), {calendarItem: calendarItem});
page.onBackPressed.connect(function() {
page.StackView.onRemoved.connect(function() {
calendarItem.destroy();
});
pageStack.pop();
calendarItem.destroy();
})
page.onDone.connect(function() {
root.rule.timeDescriptor.calendarItems.addCalendarItem(calendarItem);
@ -69,48 +89,109 @@ Page {
})
}
function createStateEvaluator(interfaceMode) {
if (interfaceMode === undefined) {
interfaceMode = false;
}
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {selectInterface: interfaceMode, showStates: true});
page.backPressed.connect(function() {
pageStack.pop();
});
page.interfaceSelected.connect(function(interfaceName) {
var stateEvaluator = root.rule.createStateEvaluator();
stateEvaluator.stateDescriptor.interfaceName = interfaceName;
selectStateDescriptorData(stateEvaluator)
});
page.thingSelected.connect(function(device) {
var stateEvaluator = root.rule.createStateEvaluator();
stateEvaluator.stateDescriptor.deviceId = device.id
selectStateDescriptorData(stateEvaluator)
})
}
function selectStateDescriptorData(stateEvaluator) {
print("Selecting stateDescriptorData for", stateEvaluator.stateDescriptor.deviceId, stateEvaluator.stateDescriptor.interfaceName)
var statePage = pageStack.push(Qt.resolvedUrl("SelectStateDescriptorPage.qml"), {text: "Select state", stateDescriptor: stateEvaluator.stateDescriptor})
statePage.backPressed.connect(function() {
statePage.StackView.onRemoved.connect(function() {
stateEvaluator.destroy();
})
pageStack.pop()
})
statePage.done.connect(function() {
root.rule.setStateEvaluator(stateEvaluator)
pageStack.pop();
pageStack.pop();
pageStack.pop();
})
}
function createInterfaceStateEvaluator() {
createStateEvaluator(true)
}
function editStateEvaluator() {
print("opening page", root.rule.stateEvaluator)
var page = pageStack.push(Qt.resolvedUrl("EditStateEvaluatorPage.qml"), { stateEvaluator: root.rule.stateEvaluator })
}
function addAction() {
var ruleAction = root.rule.actions.createNewRuleAction();
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"));
page.onBackPressed.connect(function() { pageStack.pop() })
function addRuleAction(interfaceMode) {
if (interfaceMode === undefined) {
interfaceMode = false;
}
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {selectInterface: interfaceMode, showActions: true});
page.onBackPressed.connect(function() {
pageStack.pop();
})
page.onThingSelected.connect(function(device) {
print("thing selected", device.name, device.id)
var ruleAction = root.rule.actions.createNewRuleAction();
ruleAction.deviceId = device.id;
selectRuleActionData(root.rule.actions, ruleAction)
})
page.onInterfaceSelected.connect(function(interfaceName) {
print("interface selected", interfaceName)
var ruleAction = root.rule.actions.createNewRuleAction();
ruleAction.interfaceName = interfaceName;
selectRuleActionData(root.rule.actions, ruleAction)
})
}
function addExitAction() {
var ruleAction = root.rule.exitActions.createNewRuleAction();
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"));
page.onBackPressed.connect(function() { pageStack.pop() })
function addInterfaceRuleAction() {
addRuleAction(true);
}
function addRuleExitAction(interfaceMode) {
if (interfaceMode === undefined) {
interfaceMode = false;
}
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {selectInterface: interfaceMode, showActions: true});
page.onBackPressed.connect(function() {
pageStack.pop();
})
page.onThingSelected.connect(function(device) {
print("thing selected", device.name, device.id)
var ruleAction = root.rule.exitActions.createNewRuleAction();
ruleAction.deviceId = device.id;
selectRuleActionData(root.rule.exitActions, ruleAction)
})
page.onInterfaceSelected.connect(function(interfaceName) {
print("interface selected", interfaceName)
var ruleAction = root.rule.exitActions.createNewRuleAction();
ruleAction.interfaceName = interfaceName;
selectRuleActionData(root.rule.exitActions, ruleAction)
})
}
function addInterfaceRuleExitAction() {
addRuleExitAction(true);
}
function selectRuleActionData(ruleActions, ruleAction) {
print("opening with ruleAction", ruleAction)
print("opening with ruleAction", ruleAction, ruleAction.interfaceName)
var ruleActionPage = pageStack.push(Qt.resolvedUrl("SelectRuleActionPage.qml"), {text: "Select action", ruleAction: ruleAction, rule: rule });
ruleActionPage.onBackPressed.connect(function() {
pageStack.pop(root);
ruleAction.destroy();
ruleActionPage.StackView.onRemoved.connect(function() {
ruleAction.destroy();
});
pageStack.pop();
})
ruleActionPage.onDone.connect(function() {
ruleActions.addRuleAction(ruleAction)
@ -246,8 +327,7 @@ Page {
if (root.rule.timeDescriptor.calendarItems.count > 0) {
root.addEventDescriptor()
} else {
var popup = eventQuestionDialogComponent.createObject(root)
popup.open();
pageStack.push(eventQuestionPageComponent)
}
}
}
@ -298,6 +378,9 @@ Page {
Layout.fillWidth: true
stateEvaluator: root.rule.stateEvaluator
visible: root.rule.stateEvaluator !== null
onDeleteClicked: {
root.rule.stateEvaluator = null
}
}
Label {
@ -331,12 +414,11 @@ Page {
visible: root.rule.timeDescriptor.timeEventItems.count === 0 || root.rule.stateEvaluator === null
onClicked: {
if (root.rule.timeDescriptor.timeEventItems.count > 0) {
root.rule.createStateEvaluator()
root.rule.setStateEvaluator(root.rule.createStateEvaluator());
} else if (root.rule.stateEvaluator !== null) {
root.addCalendarItem();
} else {
var popup = stateQuestionDialogComponent.createObject(root)
popup.open()
pageStack.push(stateQuestionPageComponent)
}
}
}
@ -369,7 +451,9 @@ Page {
Layout.fillWidth: true
Layout.margins: app.margins
text: actionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...")
onClicked: root.addAction();
onClicked: {
var page = pageStack.push(ruleActionQuestionPageComponent, {exitAction: false});
}
visible: root.actionsVisible
}
@ -400,7 +484,9 @@ Page {
Layout.fillWidth: true
Layout.margins: app.margins
text: actionsRepeater.count == 0 ? qsTr("Add an action...") : qsTr("Add another action...")
onClicked: root.addExitAction();
onClicked: {
var page = pageStack.push(ruleActionQuestionPageComponent, {exitAction: true});
}
visible: root.exitActionsVisible
}
}
@ -419,134 +505,162 @@ Page {
}
Component {
id: eventQuestionDialogComponent
MeaDialog {
id: questionDialog
title: qsTr("Add event...")
standardButtons: Dialog.Cancel
Button {
Layout.fillWidth: true
Layout.preferredHeight: (app.largeFont * 2) + (app.margins * 3)
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/event.svg"
color: "black"
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("When one of my things triggers an event")
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
}
}
onClicked: {
root.addEventDescriptor()
questionDialog.close()
}
id: eventQuestionPageComponent
Page {
header: GuhHeader {
text: qsTr("Add event")
onBackPressed: pageStack.pop()
}
Label {
text: qsTr("or")
Layout.fillWidth: true
Layout.margins: app.margins
horizontalAlignment: Text.AlignHCenter
}
ColumnLayout {
anchors.fill: parent
Button {
Layout.fillWidth: true
Layout.preferredHeight: (app.largeFont * 2) + (app.margins * 3)
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/alarm-clock.svg"
color: "black"
Repeater {
model: ListModel {
ListElement {
iconName: "../images/event.svg"
text: qsTr("When one of my things triggers an event")
method: "addEventDescriptor"
minimumJsonRpcVersion: "1.0"
}
ListElement {
iconName: "../images/event-interface.svg"
text: qsTr("When a thing of a given type triggers an event")
method: "addInterfaceEventDescriptor"
minimumJsonRpcVersion: "1.5"
}
ListElement {
iconName: "../images/alarm-clock.svg"
text: qsTr("At a particular time or date")
method: "addTimeEventItem"
minimumJsonRpcVersion: "1.0"
}
}
Label {
delegate: MeaListItemDelegate {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("At a particular time or date")
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
iconName: model.iconName
text: model.text
progressive: true
iconSize: app.iconSize * 2
visible: Engine.jsonRpcClient.ensureServerVersion(model.minimumJsonRpcVersion)
onClicked: {
root[model.method]()
}
}
}
onClicked: {
root.addTimeEventItem()
questionDialog.close()
}
}
}
}
Component {
id: stateQuestionDialogComponent
MeaDialog {
id: questionDialog
title: qsTr("Add condition...")
standardButtons: Dialog.Cancel
id: stateQuestionPageComponent
Page {
header: GuhHeader {
text: qsTr("Add condition...")
Button {
Layout.fillWidth: true
Layout.preferredHeight: (app.largeFont * 2) + (app.margins * 3)
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/state.svg"
color: "black"
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("When one of my things is in a certain state")
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
}
}
onClicked: {
root.rule.createStateEvaluator()
questionDialog.close()
}
onBackPressed: pageStack.pop()
}
Label {
text: qsTr("or")
Layout.fillWidth: true
Layout.margins: app.margins
horizontalAlignment: Text.AlignHCenter
}
ColumnLayout {
anchors.fill: parent
Button {
Layout.fillWidth: true
Layout.preferredHeight: (app.largeFont * 2) + (app.margins * 3)
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: height
name: "../images/clock-app-symbolic.svg"
color: "black"
Repeater {
model: ListModel {
ListElement {
iconName: "../images/state.svg"
text: qsTr("When one of my things is in a certain state")
method: "createStateEvaluator"
minimumJsonRpcVersion: "1.0"
}
ListElement {
iconName: "../images/state-interface.svg"
text: qsTr("When a thing of a given type enters a state")
method: "createInterfaceStateEvaluator"
minimumJsonRpcVersion: "1.5"
}
ListElement {
iconName: "../images/clock-app-symbolic.svg"
text: qsTr("During a given time")
method: "addCalendarItem"
minimumJsonRpcVersion: "1.0"
}
}
Label {
delegate: MeaListItemDelegate {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("During a given time")
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
iconName: model.iconName
text: model.text
progressive: true
iconSize: app.iconSize * 2
visible: Engine.jsonRpcClient.ensureServerVersion(model.minimumJsonRpcVersion)
onClicked: {
root[model.method]()
}
}
}
onClicked: {
root.addCalendarItem()
questionDialog.close()
}
}
}
Component {
id: ruleActionQuestionPageComponent
Page {
id: ruleActionQuestionPage
property bool exitAction: false
header: GuhHeader {
text: qsTr("Add action...")
onBackPressed: pageStack.pop()
}
ColumnLayout {
anchors.fill: parent
Repeater {
model: ListModel {
ListElement {
iconName: "../images/action.svg"
text: qsTr("Execute an action on of my things")
method: "addRuleAction"
isExitAction: false
minimumJsonRpcVersion: "1.0"
}
ListElement {
iconName: "../images/action-interface.svg"
text: qsTr("Execute an action on an entire kind of things")
method: "addInterfaceRuleAction"
isExitAction: false
minimumJsonRpcVersion: "1.5"
}
ListElement {
iconName: "../images/action.svg"
text: qsTr("Execute an action on of my things")
method: "addRuleExitAction"
isExitAction: true
minimumJsonRpcVersion: "1.0"
}
ListElement {
iconName: "../images/action-interface.svg"
text: qsTr("Execute an action on an entire kind of things")
method: "addInterfaceRuleExitAction"
isExitAction: true
minimumJsonRpcVersion: "1.5"
}
}
delegate: MeaListItemDelegate {
Layout.fillWidth: true
iconName: model.iconName
text: model.text
progressive: true
iconSize: app.iconSize * 2
visible: ruleActionQuestionPage.exitAction === model.isExitAction && Engine.jsonRpcClient.ensureServerVersion(model.minimumJsonRpcVersion)
onClicked: {
root[model.method]()
}
}
}
}
}

View File

@ -16,5 +16,6 @@ Page {
StateEvaluatorDelegate {
width: parent.width
stateEvaluator: root.stateEvaluator
canDelete: false
}
}

View File

@ -4,96 +4,68 @@ import QtQuick.Layouts 1.3
import Mea 1.0
import "../components"
SwipeDelegate {
MeaListItemDelegate {
id: root
implicitHeight: app.delegateHeight
canDelete: true
progressive: false
property var eventDescriptor: null
readonly property var device: eventDescriptor ? Engine.deviceManager.devices.getDevice(eventDescriptor.deviceId) : null
readonly property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
readonly property var iface: eventDescriptor.interfaceName ? Interfaces.findByName(eventDescriptor.interfaceName) : null
readonly property var eventType: deviceClass ? deviceClass.eventTypes.getEventType(eventDescriptor.eventTypeId)
: iface ? iface.eventTypes.findByName(eventDescriptor.interfaceEvent) : null
: iface ? iface.eventTypes.findByName(eventDescriptor.interfaceEvent) : null
signal removeEventDescriptor()
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: "../images/event.svg"
color: app.guhAccent
}
onDeleteClicked: root.removeEventDescriptor()
ColumnLayout {
Label {
text: qsTr("%1 - %2").arg(root.device ? root.device.name : root.iface.displayName).arg(root.eventType.displayName)
Layout.fillWidth: true
elide: Text.ElideRight
iconName: root.device ? "../images/event.svg" : "../images/event-interface.svg"
text: qsTr("%1 - %2").arg(root.device ? root.device.name : root.iface.displayName).arg(root.eventType.displayName)
subText: {
var ret = qsTr("anytime");
for (var i = 0; i < root.eventDescriptor.paramDescriptors.count; i++) {
var paramDescriptor = root.eventDescriptor.paramDescriptors.get(i)
var operatorString;
switch (paramDescriptor.operatorType) {
case ParamDescriptor.ValueOperatorEquals:
operatorString = " = ";
break;
case ParamDescriptor.ValueOperatorNotEquals:
operatorString = " != ";
break;
case ParamDescriptor.ValueOperatorGreater:
operatorString = " > ";
break;
case ParamDescriptor.ValueOperatorGreaterOrEqual:
operatorString = " >= ";
break;
case ParamDescriptor.ValueOperatorLess:
operatorString = " < ";
break;
case ParamDescriptor.ValueOperatorLessOrEqual:
operatorString = " <= ";
break;
default:
operatorString = " ? ";
}
Label {
Layout.fillWidth: true
elide: Text.ElideRight
font.pixelSize: app.smallFont
text: {
var ret = qsTr("anytime");
for (var i = 0; i < root.eventDescriptor.paramDescriptors.count; i++) {
var paramDescriptor = root.eventDescriptor.paramDescriptors.get(i)
var operatorString;
switch (paramDescriptor.operatorType) {
case ParamDescriptor.ValueOperatorEquals:
operatorString = " = ";
break;
case ParamDescriptor.ValueOperatorNotEquals:
operatorString = " != ";
break;
case ParamDescriptor.ValueOperatorGreater:
operatorString = " > ";
break;
case ParamDescriptor.ValueOperatorGreaterOrEqual:
operatorString = " >= ";
break;
case ParamDescriptor.ValueOperatorLess:
operatorString = " < ";
break;
case ParamDescriptor.ValueOperatorLessOrEqual:
operatorString = " <= ";
break;
default:
operatorString = " ? ";
}
if (i === 0) {
// TRANSLATORS: example: "only if temperature > 5"
ret = qsTr("only if %1 %2 %3")
.arg(root.eventType.paramTypes.getParamType(paramDescriptor.paramTypeId).displayName)
.arg(operatorString)
.arg(paramDescriptor.value)
} else {
// TRANSLATORS: example: "and temperature > 5"
ret += " " + qsTr("and %1 %2 %3")
.arg(root.eventType.paramTypes.getParamType(paramDescriptor.paramTypeId).displayName)
.arg(operatorString)
.arg(model.value)
}
}
return ret;
}
if (i === 0) {
// TRANSLATORS: example: "only if temperature > 5"
ret = qsTr("only if %1 %2 %3")
.arg(root.eventType.paramTypes.getParamType(paramDescriptor.paramTypeId).displayName)
.arg(operatorString)
.arg(paramDescriptor.value)
} else {
// TRANSLATORS: example: "and temperature > 5"
ret += " " + qsTr("and %1 %2 %3")
.arg(root.eventType.paramTypes.getParamType(paramDescriptor.paramTypeId).displayName)
.arg(operatorString)
.arg(model.value)
}
}
}
swipe.right: MouseArea {
height: root.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.removeEventDescriptor()
return ret;
}
}

View File

@ -4,9 +4,12 @@ import QtQuick.Layouts 1.3
import Mea 1.0
import "../components"
SwipeDelegate {
MeaListItemDelegate {
id: root
implicitHeight: app.delegateHeight
canDelete: true
progressive: false
property var ruleAction: null
property var device: ruleAction.deviceId ? Engine.deviceManager.devices.getDevice(ruleAction.deviceId) : null
@ -17,50 +20,19 @@ SwipeDelegate {
signal removeRuleAction()
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: "../images/action.svg"
color: app.guhAccent
}
onDeleteClicked: root.removeRuleAction()
ColumnLayout {
Label {
Layout.fillWidth: true
elide: Text.ElideRight
text: qsTr("%1 - %2").arg(root.device ? root.device.name : root.iface.displayName).arg(root.actionType.displayName)
}
Label {
Layout.fillWidth: true
elide: Text.ElideRight
font.pixelSize: app.smallFont
text: {
var ret = [];
for (var i = 0; i < root.ruleAction.ruleActionParams.count; i++) {
var ruleActionParam = root.ruleAction.ruleActionParams.get(i)
var paramString = qsTr("%1: %2")
.arg(root.actionType.paramTypes.getParamType(ruleActionParam.paramTypeId).displayName)
.arg(ruleActionParam.eventParamTypeId.length > 0 ? qsTr("value from event") : ruleActionParam.value)
ret.push(paramString)
}
return ret.join(', ')
}
}
iconName: root.device ? "../images/action.svg" : "../images/action-interface.svg"
text: qsTr("%1 - %2").arg(root.device ? root.device.name : root.iface.displayName).arg(root.actionType.displayName)
subText: {
var ret = [];
for (var i = 0; i < root.ruleAction.ruleActionParams.count; i++) {
var ruleActionParam = root.ruleAction.ruleActionParams.get(i)
var paramString = qsTr("%1: %2")
.arg(root.actionType.paramTypes.getParamType(ruleActionParam.paramTypeId).displayName)
.arg(ruleActionParam.eventParamTypeId.length > 0 ? qsTr("value from event") : ruleActionParam.value)
ret.push(paramString)
}
}
swipe.right: MouseArea {
height: root.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.removeRuleAction()
return ret.join(', ')
}
}

View File

@ -34,20 +34,25 @@ Page {
}
ListModel {
id: eventTemplateModel
ListElement { interfaceName: "temperaturesensor"; text: qsTr("When it's freezing..."); event: "freeze"}
ListElement { interfaceName: "battery"; text: qsTr("When the device runs out of battery..."); event: "lowBattery"}
ListElement { interfaceName: "weather"; text: qsTr("When it starts raining..."); event: "rain" }
id: generatedModel
ListElement { displayName: ""; eventTypeId: "" }
}
function buildInterface() {
if (header.interfacesMode) {
if (root.device) {
generatedModel.clear();
for (var i = 0; i < Interfaces.count; i++) {
if (deviceClass.interfaces.indexOf(Interfaces.get(i).name) >= 0) {
actualModel.append(Interfaces.get(i))
var iface = Interfaces.get(i);
if (root.deviceClass.interfaces.indexOf(iface.name) >= 0) {
for (var j = 0; j < iface.eventTypes.count; j++) {
var ifaceEt = iface.eventTypes.get(j);
var dcEt = root.deviceClass.eventTypes.findByName(ifaceEt.name)
generatedModel.append({displayName: ifaceEt.displayName, eventTypeId: dcEt.id})
}
}
}
listView.model = generatedModel
} else if (root.eventDescriptor.interfaceName !== "") {
listView.model = Interfaces.findByName(root.eventDescriptor.interfaceName).eventTypes
} else {
@ -70,16 +75,17 @@ Page {
onClicked: {
if (header.interfacesMode) {
if (root.device) {
print("selected:", model.event)
switch (model.event) {
case "lowBattery":
var eventType = root.deviceClass.eventTypes.findByName("batteryCritical")
root.eventDescriptor.eventTypeId = eventType.id;
// root.eventDescriptor.paramDescriptors.setParamDescriptor(eventType.paramTypes.get(0).paramTypeId, 0, ParamDescriptors.ValueOperatorLessOrEqual)
root.eventDescriptor.eventTypeId = model.eventTypeId;
var eventType = root.deviceClass.eventTypes.getEventType(model.eventTypeId)
if (eventType.paramTypes.count > 0) {
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectEventDescriptorParamsPage.qml"), {eventDescriptor: root.eventDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else {
root.done();
break;
default:
console.warn("FIXME: Unhandled interface event");
}
} else if (root.eventDescriptor.interfaceName !== "") {
root.eventDescriptor.interfaceEvent = model.name;

View File

@ -19,7 +19,7 @@ Page {
signal completed();
header: GuhHeader {
text: "params"
text: "Options"
onBackPressed: root.backPressed();
}
@ -60,11 +60,16 @@ Page {
Layout.fillWidth: true
Layout.margins: app.margins
onClicked: {
var params = [];
root.eventDescriptor.paramDescriptors.clear();
for (var i = 0; i < delegateRepeater.count; i++) {
var paramDelegate = delegateRepeater.itemAt(i);
if (paramDelegate.considerParam) {
root.eventDescriptor.paramDescriptors.setParamDescriptor(paramDelegate.paramType.id, paramDelegate.value, paramDelegate.operatorType)
if (root.device) {
root.eventDescriptor.paramDescriptors.setParamDescriptor(paramDelegate.paramType.id, paramDelegate.value, paramDelegate.operatorType)
} else if (root.iface) {
print("setting param descriptors by name", root.eventType.paramTypes.get(i), root.eventType.paramTypes.get(i).name)
root.eventDescriptor.paramDescriptors.setParamDescriptorByName(root.eventType.paramTypes.get(i).name, paramDelegate.value, paramDelegate.operatorType)
}
}
}
root.completed()

View File

@ -26,38 +26,39 @@ Page {
id: header
onBackPressed: root.backPressed();
property bool interfacesMode: false//root.ruleAction.interfaceName !== ""
property bool interfacesMode: root.ruleAction.interfaceName !== ""
onInterfacesModeChanged: root.buildInterface()
HeaderButton {
imageSource: header.interfacesMode ? "../images/view-expand.svg" : "../images/view-collapse.svg"
visible: root.ruleAction.deviceId || root.ruleAction.interfaceName === ""
visible: root.ruleAction.interfaceName === ""
onClicked: header.interfacesMode = !header.interfacesMode
}
}
// ListModel {
// id: actionTemplateModel
// ListElement { interfaceName: "light"; text: "Switch light"; identifier: "switchLight"}
// ListElement { interfaceName: "dimmablelight"; text: "Dim light"; identifier: "dimLight"}
// ListElement { interfaceName: "colorlight"; text: "Set light color"; identifier: "colorLight" }
// ListElement { interfaceName: "mediacontroller"; text: "Pause playback"; identifier: "pausePlayback" }
// ListElement { interfaceName: "mediacontroller"; text: "Resume playback"; identifier: "resumePlayback" }
// ListElement { interfaceName: "extendedvolumecontroller"; text: "Set volume"; identifier: "setVolume" }
// ListElement { interfaceName: "extendedvolumecontroller"; text: "Mute"; identifier: "mute" }
// ListElement { interfaceName: "extendedvolumecontroller"; text: "Unmute"; identifier: "unmute" }
// ListElement { interfaceName: "notifications"; text: "Notify me"; identifier: "notify" }
// }
ListModel {
id: generatedModel
ListElement { displayName: ""; actionTypeId: "" }
}
function buildInterface() {
print("building iface", root.ruleAction, root.ruleAction.interfaceName, header.interfacesMode, root.ruleAction.interfaceName === "")
if (header.interfacesMode) {
if (root.device) {
generatedModel.clear();
for (var i = 0; i < Interfaces.count; i++) {
if (deviceClass.interfaces.indexOf(Interfaces.get(i).interfaceName) >= 0) {
actualModel.append(Interfaces.get(i))
var iface = Interfaces.get(i);
if (root.deviceClass.interfaces.indexOf(iface.name) >= 0) {
for (var j = 0; j < iface.actionTypes.count; j++) {
var ifaceAt = iface.actionTypes.get(j);
var dcAt = root.deviceClass.actionTypes.findByName(ifaceAt.name)
generatedModel.append({displayName: ifaceAt.displayName, actionTypeId: dcAt.id})
}
}
}
listView.model = generatedModel
} else if (root.ruleAction.interfaceName !== "") {
print("showing actions for interface", root.ruleAction.interfaceName)
listView.model = Interfaces.findByName(root.ruleAction.interfaceName).actionTypes
} else {
console.warn("You need to set device or interfaceName");
@ -79,13 +80,17 @@ Page {
onClicked: {
if (header.interfacesMode) {
if (root.device) {
print("selected:", model.identifier)
switch (model.identfier) {
case "switchLight":
root.ruleAction.actionTypeId = model.actionTypeId;
var actionType = root.deviceClass.actionTypes.getActionType(model.actionTypeId)
if (actionType.paramTypes.count > 0) {
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectRuleActionParamsPage.qml"), {ruleAction: root.ruleAction, rule: root.rule})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else {
root.done();
break;
default:
console.warn("FIXME: Unhandled interface action");
}
} else if (root.ruleAction.interfaceName !== "") {
root.ruleAction.interfaceAction = model.name;

View File

@ -10,7 +10,7 @@ Page {
// Needs to be set and have rule.ruleActions filled in with deviceId and actionTypeId or interfaceName and interfaceAction
property var ruleAction: null
// optionally a rule which will be used to propse event's params as param values
// optionally a rule which will be used to propose event's params as param values
property var rule: null
readonly property var device: ruleAction && ruleAction.deviceId ? Engine.deviceManager.devices.getDevice(ruleAction.deviceId) : null
@ -81,9 +81,9 @@ Page {
model: EventDescriptorParamsFilterModel {
id: eventDescriptorParamsFilterModel
eventDescriptor: root.rule.eventDescriptors.count === 1 ? root.rule.eventDescriptors.get(0) : null
property var device: Engine.deviceManager.devices.getDevice(eventDescriptor.deviceId)
property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
property var eventType: deviceClass.eventTypes.getEventType(eventDescriptor.eventTypeId)
property var device: eventDescriptor ? Engine.deviceManager.devices.getDevice(eventDescriptor.deviceId) : null
property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
property var eventType: deviceClass ? deviceClass.eventTypes.getEventType(eventDescriptor.eventTypeId) : null
property var paramDescriptor: eventDescriptorParamsFilterModel.eventType.paramTypes.get(eventParamsComboBox.currentIndex)
property var paramTypeId: paramDescriptor.id
}
@ -116,7 +116,11 @@ Page {
for (var i = 0; i < delegateRepeater.count; i++) {
var paramDelegate = delegateRepeater.itemAt(i);
if (paramDelegate.type === "static") {
root.ruleAction.ruleActionParams.setRuleActionParam(paramDelegate.paramType.id, paramDelegate.value)
if (root.device) {
root.ruleAction.ruleActionParams.setRuleActionParam(paramDelegate.paramType.id, paramDelegate.value)
} else if (root.iface) {
root.ruleAction.ruleActionParams.setRuleActionParamByName(root.actionType.paramTypes.get(i).name, paramDelegate.value)
}
} else if (paramDelegate.type === "event") {
print("adding event based rule action param", paramDelegate.paramType.id, paramDelegate.eventType.id, paramDelegate.eventParamTypeId)
root.ruleAction.ruleActionParams.setRuleActionParamEvent(paramDelegate.paramType.id, paramDelegate.eventType.id, paramDelegate.eventParamTypeId)

View File

@ -16,29 +16,105 @@ Page {
signal backPressed();
signal done();
onStateDescriptorChanged: buildInterface()
Component.onCompleted: buildInterface()
header: GuhHeader {
id: header
onBackPressed: root.backPressed();
property bool interfacesMode: root.stateDescriptor && root.stateDescriptor.interfaceName && root.stateDescriptor.interfaceName.length > 0
onInterfacesModeChanged: root.buildInterface()
HeaderButton {
imageSource: header.interfacesMode ? "../images/view-expand.svg" : "../images/view-collapse.svg"
visible: root.stateDescriptor && root.stateDescriptor.interfaceName.length === 0
onClicked: header.interfacesMode = !header.interfacesMode
}
}
ListModel {
id: generatedModel
ListElement { displayName: ""; stateTypeId: "" }
}
function buildInterface() {
print("building interface:", header.interfacesMode, root.stateDescriptor, root.stateDescriptor.interfaceName)
if (header.interfacesMode) {
if (root.device) {
generatedModel.clear();
for (var i = 0; i < Interfaces.count; i++) {
var iface = Interfaces.get(i);
if (root.deviceClass.interfaces.indexOf(iface.name) >= 0) {
print("root has device class:", iface.name, iface.stateTypes.count)
for (var j = 0; j < iface.stateTypes.count; j++) {
var ifaceSt = iface.stateTypes.get(j);
print("ifaceSt:", ifaceSt, j, iface.stateTypes.count)
var dcSt = root.deviceClass.stateTypes.findByName(ifaceSt.name)
print("adding:", ifaceSt.displayName, dcSt.id)
generatedModel.append({displayName: ifaceSt.displayName, stateTypeId: dcSt.id})
}
}
}
listView.model = generatedModel
} else if (root.stateDescriptor.interfaceName !== "") {
listView.model = Interfaces.findByName(root.stateDescriptor.interfaceName).stateTypes
} else {
console.warn("You need to set device or interfaceName");
}
} else {
if (root.device) {
listView.model = deviceClass.stateTypes;
}
}
}
ListView {
id: listView
anchors.fill: parent
model: root.deviceClass.stateTypes
delegate: ItemDelegate {
text: model.displayName
width: parent.width
onClicked: {
var stateType = root.deviceClass.stateTypes.getStateType(model.id);
console.log("StateType", stateType.id, "selected.")
root.stateDescriptor.stateTypeId = stateType.id;
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectStateDescriptorParamsPage.qml"), {stateDescriptor: root.stateDescriptor})
paramsPage.onBackPressed.connect(function() { pageStack.pop(); });
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
if (header.interfacesMode) {
if (root.device) {
print("selected:", model.stateTypeId)
root.stateDescriptor.stateTypeId = model.stateTypeId;
var stateType = root.deviceClass.stateTypes.getStateType(model.stateTypeId)
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectStateDescriptorParamsPage.qml"), {stateDescriptor: root.stateDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else if (root.stateDescriptor.interfaceName !== "") {
root.stateDescriptor.interfaceState = model.name;
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectStateDescriptorParamsPage.qml"), {stateDescriptor: root.stateDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
} else {
console.warn("Neither deviceId not interfaceName set. Cannot continue...");
}
} else {
if (root.device) {
var stateType = root.deviceClass.stateTypes.getStateType(model.id);
root.stateDescriptor.stateTypeId = model.id;
var paramsPage = pageStack.push(Qt.resolvedUrl("SelectStateDescriptorParamsPage.qml"), {stateDescriptor: root.stateDescriptor})
paramsPage.onBackPressed.connect(function() {pageStack.pop()});
paramsPage.onCompleted.connect(function() {
pageStack.pop();
root.done();
})
print("have type", stateType.id)
} else {
console.warn("FIXME: not implemented yet");
}
}
}
}
}

View File

@ -11,13 +11,15 @@ Page {
property var stateDescriptor: null
readonly property var device: stateDescriptor && stateDescriptor.deviceId ? Engine.deviceManager.devices.getDevice(stateDescriptor.deviceId) : null
readonly property var stateType: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId).stateTypes.getStateType(stateDescriptor.stateTypeId) : null
readonly property var iface: stateDescriptor && stateDescriptor.interfaceName ? Interfaces.findByName(stateDescriptor.interfaceName) : null
readonly property var stateType: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId).stateTypes.getStateType(stateDescriptor.stateTypeId)
: iface ? iface.stateTypes.findByName(stateDescriptor.interfaceState) : null
signal backPressed();
signal completed();
header: GuhHeader {
text: qsTr("params")
text: qsTr("Options")
onBackPressed: root.backPressed();
}

View File

@ -8,45 +8,41 @@ import Mea 1.0
Page {
id: root
property bool selectInterface: false
signal backPressed();
signal thingSelected(var device);
signal interfaceSelected(string interfaceName);
property alias showEvents: interfacesProxy.showEvents
property alias showActions: interfacesProxy.showActions
property alias showStates: interfacesProxy.showStates
header: GuhHeader {
text: qsTr("Select a thing")
text: root.selectInterface ? qsTr("Select a kind of things") : qsTr("Select a thing")
onBackPressed: root.backPressed()
}
InterfacesProxy {
id: interfacesProxy
devicesFilter: Engine.deviceManager.devices
}
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
// TODO: unfinished, disabled for now
visible: false
RadioButton {
id: thingButton
text: qsTr("A specific thing")
checked: true
}
RadioButton {
id: interfacesButton
text: qsTr("A group of things")
}
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: thingButton.checked ? Engine.deviceManager.devices : Interfaces
model: root.selectInterface ? interfacesProxy : Engine.deviceManager.devices
clip: true
delegate: ThingDelegate {
name: thingButton.checked ? model.name : model.displayName
interfaces: model.interfaces
delegate: MeaListItemDelegate {
width: parent.width
text: root.selectInterface ? model.displayName : model.name
iconName: root.selectInterface ? app.interfaceToIcon(model.name) : app.interfacesToIcon(model.interfaces)
onClicked: {
if (thingButton.checked) {
root.thingSelected(Engine.deviceManager.devices.get(index))
if (root.selectInterface) {
root.interfaceSelected(interfacesProxy.get(index).name)
} else {
root.interfaceSelected(Interfaces.get(index).name)
root.thingSelected(Engine.deviceManager.devices.get(index))
}
}
}

View File

@ -2,17 +2,23 @@ import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import Mea 1.0
import "../components"
SwipeDelegate {
id: root
Layout.fillWidth: true
clip: true
property var stateEvaluator: null
property bool showChilds: false
readonly property var device: stateEvaluator ? Engine.deviceManager.devices.getDevice(stateEvaluator.stateDescriptor.deviceId) : null
readonly property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
readonly property var stateType: deviceClass ? deviceClass.stateTypes.getStateType(stateEvaluator.stateDescriptor.stateTypeId) : null
readonly property var iface: stateEvaluator ? Interfaces.findByName(stateEvaluator.stateDescriptor.interfaceName) : null
readonly property var stateType: deviceClass ? deviceClass.stateTypes.getStateType(stateEvaluator.stateDescriptor.stateTypeId)
: iface ? iface.stateTypes.findByName(stateEvaluator.stateDescriptor.interfaceState) : null
signal deleteClicked();
Rectangle {
anchors.fill: parent
@ -22,49 +28,24 @@ SwipeDelegate {
}
contentItem: ColumnLayout {
Label {
RowLayout {
Layout.fillWidth: true
property string operatorString: {
if (!root.stateEvaluator) {
return "";
}
switch (root.stateEvaluator.stateDescriptor.valueOperator) {
case StateDescriptor.ValueOperatorEquals:
return "=";
case StateDescriptor.ValueOperatorNotEquals:
return "!=";
case StateDescriptor.ValueOperatorGreater:
return ">";
case StateDescriptor.ValueOperatorGreaterOrEqual:
return ">=";
case StateDescriptor.ValueOperatorLess:
return "<";
case StateDescriptor.ValueOperatorLessOrEqual:
return "<=";
}
return "FIXME"
ColorIcon {
Layout.preferredHeight: childEvaluatorsRepeater.count > 0 ? app.iconSize * .6 : app.iconSize
Layout.preferredWidth: height
name: root.stateEvaluator.stateDescriptor.interfaceName.length === 0 ? "../images/state.svg" : "../images/state-interface.svg"
color: app.guhAccent
}
text: {
if (!root.device) {
return qsTr("Press to edit condition")
}
return qsTr("%1: %2 %3 %4").arg(root.device.name).arg(root.stateType.displayName).arg(operatorString).arg(root.stateEvaluator.stateDescriptor.value)
}
}
Repeater {
model: root.showChilds ? root.stateEvaluator.childEvaluators : null
delegate: Label {
Label {
Layout.fillWidth: true
property var stateEvaluator: root.stateEvaluator.childEvaluators.get(index)
property var stateDescriptor: stateEvaluator.stateDescriptor
readonly property var device: Engine.deviceManager.devices.getDevice(stateDescriptor.deviceId)
readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId)
readonly property var stateType: deviceClass.stateTypes.getStateType(stateDescriptor.stateTypeId)
font.pixelSize: childEvaluatorsRepeater.count > 0 ? app.smallFont : app.mediumFont
property string operatorString: {
switch (stateDescriptor.valueOperator) {
if (!root.stateEvaluator) {
return "";
}
switch (root.stateEvaluator.stateDescriptor.valueOperator) {
case StateDescriptor.ValueOperatorEquals:
return "=";
case StateDescriptor.ValueOperatorNotEquals:
@ -80,8 +61,90 @@ SwipeDelegate {
}
return "FIXME"
}
text: qsTr("%1 %2: %3 %4 %5%6").arg(root.stateEvaluator.stateOperator === StateEvaluator.StateOperatorAnd ? "and" : "or").arg(device.name).arg(stateType.displayName).arg(operatorString).arg(stateDescriptor.value).arg(stateEvaluator.childEvaluators.count > 0 ? "..." : "")
text: {
if (!root.stateType) {
return qsTr("Press to edit condition")
}
if (root.device) {
return qsTr("%1: %2 %3 %4").arg(root.device.name).arg(root.stateType.displayName).arg(operatorString).arg(root.stateEvaluator.stateDescriptor.value)
} else if (root.iface) {
return qsTr("%1: %2 %3 %4").arg(root.iface.displayName).arg(root.stateType.displayName).arg(operatorString).arg(root.stateEvaluator.stateDescriptor.value)
}
return "--";
}
}
}
Repeater {
id: childEvaluatorsRepeater
model: root.showChilds ? root.stateEvaluator.childEvaluators : null
delegate: RowLayout {
id: childEvaluatorDelegate
Layout.fillWidth: true
property var stateEvaluator: root.stateEvaluator.childEvaluators.get(index)
property var stateDescriptor: stateEvaluator.stateDescriptor
readonly property var device: Engine.deviceManager.devices.getDevice(stateDescriptor.deviceId)
readonly property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
readonly property var iface: Interfaces.findByName(stateEvaluator.stateDescriptor.interfaceName)
readonly property var stateType: device ? deviceClass.stateTypes.getStateType(stateDescriptor.stateTypeId)
: iface ? iface.stateTypes.findByName(stateEvaluator.stateDescriptor.interfaceState)
: null
ColorIcon {
Layout.preferredHeight: app.iconSize * .6
Layout.preferredWidth: height
name: childEvaluatorDelegate.stateDescriptor.interfaceName.length === 0 ? "../images/state.svg" : "../images/state-interface.svg"
color: app.guhAccent
}
Label {
font.pixelSize: app.smallFont
Layout.fillWidth: true
property string operatorString: {
switch (childEvaluatorDelegate.stateDescriptor.valueOperator) {
case StateDescriptor.ValueOperatorEquals:
return "=";
case StateDescriptor.ValueOperatorNotEquals:
return "!=";
case StateDescriptor.ValueOperatorGreater:
return ">";
case StateDescriptor.ValueOperatorGreaterOrEqual:
return ">=";
case StateDescriptor.ValueOperatorLess:
return "<";
case StateDescriptor.ValueOperatorLessOrEqual:
return "<=";
}
return "FIXME"
}
text: device ? ("%1 %2: %3 %4 %5%6").arg(root.stateEvaluator.stateOperator === StateEvaluator.StateOperatorAnd ? "and" : "or").arg(childEvaluatorDelegate.device.name).arg(childEvaluatorDelegate.stateType.displayName).arg(operatorString).arg(childEvaluatorDelegate.stateDescriptor.value).arg(childEvaluatorDelegate.stateEvaluator.childEvaluators.count > 0 ? "..." : "")
: iface ? ("%1 %2: %3 %4 %5%6").arg(root.stateEvaluator.stateOperator === StateEvaluator.StateOperatorAnd ? "and" : "or").arg(childEvaluatorDelegate.iface.displayName).arg(childEvaluatorDelegate.stateType.displayName).arg(operatorString).arg(childEvaluatorDelegate.stateDescriptor.value).arg(childEvaluatorDelegate.stateEvaluator.childEvaluators.count > 0 ? "..." : "")
: "???"
}
}
}
}
swipe.right: MouseArea {
height: parent.height
width: height
anchors.right: parent.right
Rectangle {
anchors.fill: parent
color: "red"
}
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "white"
}
onClicked: {
swipe.close()
root.deleteClicked();
}
}
}

View File

@ -2,8 +2,9 @@ import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import Mea 1.0
import "../components"
SwipeDelegate {
ItemDelegate {
id: root
property var stateEvaluator: null
@ -11,19 +12,54 @@ SwipeDelegate {
readonly property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
readonly property var stateType: deviceClass ? deviceClass.stateTypes.getStateType(stateEvaluator.stateDescriptor.stateTypeId) : null
property bool canDelete: true
signal deleteClicked()
function editStateDescriptor(interfaceMode) {
if (interfaceMode === undefined) {
interfaceMode = false;
}
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"), {selectInterface: interfaceMode, showStates: true});
page.backPressed.connect(function() {
pageStack.pop()
})
page.thingSelected.connect(function(device) {
root.stateEvaluator.stateDescriptor.interfaceName = "";
root.stateEvaluator.stateDescriptor.deviceId = device.id;
selectStateDescriptorData()
});
page.interfaceSelected.connect(function(interfaceName) {
root.stateEvaluator.stateDescriptor.deviceId = "";
root.stateEvaluator.stateDescriptor.interfaceName = interfaceName;
selectStateDescriptorData();
});
}
function editInterfaceStateDescriptor() {
editStateDescriptor(true)
}
function selectStateDescriptorData() {
var statePage = pageStack.push(Qt.resolvedUrl("SelectStateDescriptorPage.qml"), {text: "Select state", stateDescriptor: root.stateEvaluator.stateDescriptor})
statePage.backPressed.connect(function() {
pageStack.pop();
})
statePage.done.connect(function() {
pageStack.pop();
pageStack.pop();
pageStack.pop();
})
}
contentItem: ColumnLayout {
SimpleStateEvaluatorDelegate {
Layout.fillWidth: true
stateEvaluator: root.stateEvaluator
swipe.enabled: root.canDelete
onClicked: {
var page = pageStack.push(Qt.resolvedUrl("SelectThingPage.qml"));
page.backPressed.connect(function() {pageStack.pop()})
page.thingSelected.connect(function(device) {
root.stateEvaluator.stateDescriptor.deviceId = device.id
var statePage = pageStack.push(Qt.resolvedUrl("SelectStateDescriptorPage.qml"), {text: "Select state", stateDescriptor: root.stateEvaluator.stateDescriptor})
statePage.backPressed.connect(function() {pageStack.pop()})
statePage.done.connect(function() {pageStack.pop(); pageStack.pop()})
})
var page = pageStack.push(stateQuestionPageComponent);
}
onDeleteClicked: {
root.deleteClicked()
}
}
@ -46,6 +82,9 @@ SwipeDelegate {
onClicked: {
pageStack.push(Qt.resolvedUrl("EditStateEvaluatorPage.qml"), {stateEvaluator: stateEvaluator})
}
onDeleteClicked: {
root.stateEvaluator.childEvaluators.remove(index)
}
}
}
@ -54,7 +93,48 @@ SwipeDelegate {
text: qsTr("Add a condition")
onClicked: {
root.stateEvaluator.addChildEvaluator()
// root.editStateEvaluator()
}
}
}
Component {
id: stateQuestionPageComponent
Page {
header: GuhHeader {
text: qsTr("Edit condition...")
onBackPressed: pageStack.pop()
}
ColumnLayout {
anchors.fill: parent
Repeater {
model: ListModel {
ListElement {
iconName: "../images/state.svg"
text: qsTr("When one of my things is in a certain state")
method: "editStateDescriptor"
}
ListElement {
iconName: "../images/state-interface.svg"
text: qsTr("When a thing of a given type enters a state")
method: "editInterfaceStateDescriptor"
}
}
delegate: MeaListItemDelegate {
Layout.fillWidth: true
iconName: model.iconName
text: model.text
progressive: true
iconSize: app.iconSize * 2
onClicked: {
root[model.method]()
}
}
}
}
}
}

View File

@ -4,9 +4,11 @@ import QtQuick.Layouts 1.3
import Mea 1.0
import "../components"
SwipeDelegate {
MeaListItemDelegate{
id: root
implicitHeight: app.delegateHeight
progressive: false
canDelete: true
property var timeEventItem: null
@ -15,88 +17,7 @@ SwipeDelegate {
signal removeTimeEventItem();
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
Layout.preferredHeight: app.iconSize
Layout.preferredWidth: app.iconSize
name: "../images/alarm-clock.svg"
color: app.guhAccent
}
ColumnLayout {
Label {
Layout.fillWidth: true
elide: Text.ElideRight
text: qsTr("At %1").arg(root.isDateBased ? Qt.formatDateTime(root.timeEventItem.dateTime) : Qt.formatTime(root.timeEventItem.time))
}
Label {
Layout.fillWidth: true
text: qsTr("repeated %1").arg(repeatingString)
elide: Text.ElideRight
font.pixelSize: app.smallFont
property string repeatingString: {
switch (root.timeEventItem.repeatingOption.repeatingMode) {
case RepeatingOption.RepeatingModeNone:
return qsTr("never");
case RepeatingOption.RepeatingModeHourly:
return qsTr("hourly");
case RepeatingOption.RepeatingModeDaily:
return qsTr("daily");
case RepeatingOption.RepeatingModeWeekly:
var weekdays = []
for (var i = 0; i < root.timeEventItem.repeatingOption.weekDays.length; i++) {
switch (root.timeEventItem.repeatingOption.weekDays[i]) {
case 1:
weekdays.push(qsTr("Mon"));
break;
case 2:
weekdays.push(qsTr("Tue"));
break;
case 3:
weekdays.push(qsTr("Wed"));
break;
case 4:
weekdays.push(qsTr("Thu"));
break;
case 5:
weekdays.push(qsTr("Fri"));
break;
case 6:
weekdays.push(qsTr("Sat"));
break;
case 7:
weekdays.push(qsTr("Sun"));
break;
}
}
return qsTr("weekly on %1").arg(weekdays.join(', '));
case RepeatingOption.RepeatingModeMonthly:
return qsTr("monthly on the %1").arg(root.timeEventItem.repeatingOption.monthDays.join(', '));
case RepeatingOption.RepeatingModeYearly:
return qsTr("every year");
}
}
}
}
}
swipe.right: MouseArea {
height: root.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
onClicked: root.removeTimeEventItem()
}
onDeleteClicked: root.removeTimeEventItem()
onClicked: {
var page = pageStack.push(Qt.resolvedUrl("EditTimeEventItemPage.qml"), {timeEventItem: root.timeEventItem})
@ -106,4 +27,52 @@ SwipeDelegate {
print("timeeventItem.time is now", root.timeEventItem.time)
})
}
iconName: "../images/alarm-clock.svg"
text: qsTr("At %1").arg(root.isDateBased ? Qt.formatDateTime(root.timeEventItem.dateTime) : Qt.formatTime(root.timeEventItem.time))
subText: qsTr("repeated %1").arg(repeatingString)
property string repeatingString: {
switch (root.timeEventItem.repeatingOption.repeatingMode) {
case RepeatingOption.RepeatingModeNone:
return qsTr("never");
case RepeatingOption.RepeatingModeHourly:
return qsTr("hourly");
case RepeatingOption.RepeatingModeDaily:
return qsTr("daily");
case RepeatingOption.RepeatingModeWeekly:
var weekdays = []
for (var i = 0; i < root.timeEventItem.repeatingOption.weekDays.length; i++) {
switch (root.timeEventItem.repeatingOption.weekDays[i]) {
case 1:
weekdays.push(qsTr("Mon"));
break;
case 2:
weekdays.push(qsTr("Tue"));
break;
case 3:
weekdays.push(qsTr("Wed"));
break;
case 4:
weekdays.push(qsTr("Thu"));
break;
case 5:
weekdays.push(qsTr("Fri"));
break;
case 6:
weekdays.push(qsTr("Sat"));
break;
case 7:
weekdays.push(qsTr("Sun"));
break;
}
}
return qsTr("weekly on %1").arg(weekdays.join(', '));
case RepeatingOption.RepeatingModeMonthly:
return qsTr("monthly on the %1").arg(root.timeEventItem.repeatingOption.monthDays.join(', '));
case RepeatingOption.RepeatingModeYearly:
return qsTr("every year");
}
}
}

View File

@ -0,0 +1,39 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import Mea 1.0
import "../components"
Page {
id: root
header: GuhHeader {
text: qsTr("About %1").arg(app.systemName)
onBackPressed: pageStack.pop()
}
ColumnLayout {
anchors { left: parent.left; top: parent.top; right: parent.right }
MeaListItemDelegate {
Layout.fillWidth: true
text: qsTr("Server UUID:")
subText: Engine.jsonRpcClient.serverUuid
progressive: false
}
MeaListItemDelegate {
Layout.fillWidth: true
text: qsTr("Server version:")
subText: Engine.jsonRpcClient.serverVersion
progressive: false
}
MeaListItemDelegate {
Layout.fillWidth: true
text: qsTr("Protocol version:")
subText: Engine.jsonRpcClient.jsonRpcVersion
progressive: false
}
}
}

View File

@ -18,21 +18,11 @@ Page {
model: Engine.deviceManager.plugins
clip: true
delegate: ItemDelegate {
delegate: MeaListItemDelegate {
width: parent.width
contentItem: RowLayout {
Label {
Layout.fillWidth: true
text: model.name
}
Image {
source: "../images/next.svg"
Layout.preferredHeight: parent.height
Layout.preferredWidth: height
}
}
iconName: "../images/plugin.svg"
text: model.name
onClicked: pageStack.push(Qt.resolvedUrl("PluginParamsPage.qml"), {plugin: Engine.deviceManager.plugins.get(index)})
}
}
}