some more work

pull/1/head
Michael Zanetti 2018-02-25 00:45:52 +01:00
parent 15cdd7c91c
commit 294cff0fb3
41 changed files with 1094 additions and 95 deletions

View File

@ -6,4 +6,4 @@
androidBuildToolsVersion=26.0.1
androidCompileSdkVersion=26
buildDir=.build
qt5AndroidDir=/home/micha/Develop/Qt/5.8/android_armv7/src/android/java
qt5AndroidDir=/home/micha/Develop/Qt/5.10.1/android_armv7/src/android/java

View File

@ -216,7 +216,7 @@ void DeviceManager::pairDeviceResponse(const QVariantMap &params)
emit pairDeviceReply(params.value("params").toMap());
}
void DeviceManager::confirmPairintResponse(const QVariantMap &params)
void DeviceManager::confirmPairingResponse(const QVariantMap &params)
{
emit confirmPairingReply(params.value("params").toMap());
}
@ -231,11 +231,11 @@ void DeviceManager::addDiscoveredDevice(const QUuid &deviceClassId, const QUuid
m_jsonClient->sendCommand("Devices.AddConfiguredDevice", params);
}
void DeviceManager::pairDevice(const QUuid &deviceClassId, const QUuid &deviceDescriptorId)
void DeviceManager::pairDevice(const QUuid &deviceClassId, const QUuid &deviceDescriptorId, const QString &name)
{
qDebug() << "JsonRpc: pair device " << deviceClassId.toString();
QVariantMap params;
params.insert("name", "name");
params.insert("name", name);
params.insert("deviceClassId", deviceClassId.toString());
params.insert("deviceDescriptorId", deviceDescriptorId.toString());
m_jsonClient->sendCommand("Devices.PairDevice", params, this, "pairDeviceResponse");

View File

@ -53,7 +53,7 @@ public:
Q_INVOKABLE void addDevice(const QUuid &deviceClassId, const QString &name, const QVariantList &deviceParams);
Q_INVOKABLE void addDiscoveredDevice(const QUuid &deviceClassId, const QUuid &deviceDescriptorId, const QString &name);
Q_INVOKABLE void pairDevice(const QUuid &deviceClassId, const QUuid &deviceDescriptorId);
Q_INVOKABLE void pairDevice(const QUuid &deviceClassId, const QUuid &deviceDescriptorId, const QString &name);
Q_INVOKABLE void confirmPairing(const QUuid &pairingTransactionId, const QString &secret = QString());
Q_INVOKABLE void removeDevice(const QUuid &deviceId);
Q_INVOKABLE void executeAction(const QUuid &deviceId, const QUuid &actionTypeId, const QVariantList &params = QVariantList());
@ -67,7 +67,7 @@ private:
Q_INVOKABLE void addDeviceResponse(const QVariantMap &params);
Q_INVOKABLE void removeDeviceResponse(const QVariantMap &params);
Q_INVOKABLE void pairDeviceResponse(const QVariantMap &params);
Q_INVOKABLE void confirmPairintResponse(const QVariantMap &params);
Q_INVOKABLE void confirmPairingResponse(const QVariantMap &params);
signals:
void pairDeviceReply(const QVariantMap &params);

View File

@ -22,6 +22,7 @@
#include "tcpsocketinterface.h"
#include "rulemanager.h"
#include "logmanager.h"
Engine* Engine::s_instance = 0;
@ -56,6 +57,11 @@ JsonRpcClient *Engine::jsonRpcClient() const
return m_jsonRpcClient;
}
LogManager *Engine::logManager() const
{
return m_logManager;
}
GuhConnection *Engine::connection() const
{
return m_connection;
@ -66,7 +72,8 @@ Engine::Engine(QObject *parent) :
m_connection(new GuhConnection(this)),
m_jsonRpcClient(new JsonRpcClient(m_connection, this)),
m_deviceManager(new DeviceManager(m_jsonRpcClient, this)),
m_ruleManager(new RuleManager(m_jsonRpcClient, this))
m_ruleManager(new RuleManager(m_jsonRpcClient, this)),
m_logManager(new LogManager(m_jsonRpcClient, this))
{
connect(m_jsonRpcClient, &JsonRpcClient::connectedChanged, this, &Engine::onConnectedChanged);
connect(m_jsonRpcClient, &JsonRpcClient::authenticationRequiredChanged, this, &Engine::onConnectedChanged);

View File

@ -29,8 +29,8 @@
#include "guhinterface.h"
#include "jsonrpc/jsonrpcclient.h"
class RuleManager;
class LogManager;
class Engine : public QObject
{
@ -51,6 +51,7 @@ public:
DeviceManager *deviceManager() const;
RuleManager *ruleManager() const;
JsonRpcClient *jsonRpcClient() const;
LogManager *logManager() const;
private:
explicit Engine(QObject *parent = 0);
@ -60,6 +61,7 @@ private:
JsonRpcClient *m_jsonRpcClient;
DeviceManager *m_deviceManager;
RuleManager *m_ruleManager;
LogManager *m_logManager;
private slots:
void onConnectedChanged();

View File

@ -35,7 +35,8 @@ HEADERS += engine.h \
models/rulesfiltermodel.h \
models/logsmodel.h \
models/valuelogsproxymodel.h \
discovery/guhdiscovery.h
discovery/guhdiscovery.h \
logmanager.h
SOURCES += main.cpp \
@ -66,7 +67,8 @@ SOURCES += main.cpp \
models/rulesfiltermodel.cpp \
models/logsmodel.cpp \
models/valuelogsproxymodel.cpp \
discovery/guhdiscovery.cpp
discovery/guhdiscovery.cpp \
logmanager.cpp
withavahi {
DEFINES += WITH_AVAHI

View File

@ -69,8 +69,8 @@ void JsonRpcClient::sendCommand(const QString &method, QObject *caller, const QS
void JsonRpcClient::setNotificationsEnabled(bool enabled)
{
QVariantMap params;
params.insert("notificationsEnabled", enabled);
JsonRpcReply *reply = createReply("JSONRPC.SetNotificationsEnabled", params, this, "setNotificationsEnabledResponse");
params.insert("enabled", enabled);
JsonRpcReply *reply = createReply("JSONRPC.SetNotificationStatus", params, this, "setNotificationsEnabledResponse");
m_replies.insert(reply->commandId(), reply);
sendRequest(reply->requestMap());
}
@ -239,6 +239,13 @@ void JsonRpcClient::dataReceived(const QByteArray &data)
if (reply) {
// qDebug() << QString("JsonRpc: got response for %1.%2: %3").arg(reply->nameSpace(), reply->method(), QString::fromUtf8(jsonDoc.toJson(QJsonDocument::Indented))) << reply->callback() << reply->callback();
if (dataMap.value("status").toString() == "unauthorized") {
qWarning() << "Something's off with the token";
m_authenticationRequired = true;
m_token.clear();
emit authenticationRequiredChanged();
}
if (reply->caller() != nullptr && !reply->callback().isEmpty()) {
QMetaObject::invokeMethod(reply->caller(), reply->callback().toLatin1().data(), Q_ARG(QVariantMap, dataMap));
}

View File

@ -0,0 +1,20 @@
#include "logmanager.h"
#include "engine.h"
LogManager::LogManager(JsonRpcClient *jsonClient, QObject *parent) :
JsonHandler(parent),
m_client(jsonClient)
{
m_client->registerNotificationHandler(this, "notificationReceived");
}
QString LogManager::nameSpace() const
{
return "Logging";
}
void LogManager::notificationReceived(const QVariantMap &data)
{
emit logEntryReceived(data.value("params").toMap().value("logEntry").toMap());
}

28
guh-control/logmanager.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef LOGMANAGER_H
#define LOGMANAGER_H
#include <QObject>
#include "jsonrpc/jsonhandler.h"
class JsonRpcClient;
class LogManager : public JsonHandler
{
Q_OBJECT
public:
explicit LogManager(JsonRpcClient *jsonClient, QObject *parent = nullptr);
QString nameSpace() const override;
signals:
void logEntryReceived(const QVariantMap &data);
private:
Q_INVOKABLE void notificationReceived(const QVariantMap &data);
private:
JsonRpcClient *m_client = nullptr;
};
#endif // LOGMANAGER_H

View File

@ -1,10 +1,11 @@
#include "logsmodel.h"
#include "engine.h"
#include "logmanager.h"
LogsModel::LogsModel(QObject *parent) : QAbstractListModel(parent)
{
connect(Engine::instance()->logManager(), &LogManager::logEntryReceived, this, &LogsModel::newLogEntryReceived);
}
bool LogsModel::busy() const
@ -19,16 +20,48 @@ int LogsModel::rowCount(const QModelIndex &parent) const
QVariant LogsModel::data(const QModelIndex &index, int role) const
{
switch (role) {
case RoleTimestamp:
return m_list.at(index.row())->timestamp();
case RoleValue:
return m_list.at(index.row())->value();
case RoleDeviceId:
return m_list.at(index.row())->deviceId();
case RoleTypeId:
return m_list.at(index.row())->typeId();
case RoleSource:
return m_list.at(index.row())->source();
case RoleLoggingEventType:
return m_list.at(index.row())->loggingEventType();
}
return QVariant();
}
QHash<int, QByteArray> LogsModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles.insert(RoleTimestamp, "timestamp");
roles.insert(RoleValue, "value");
roles.insert(RoleDeviceId, "deviceId");
roles.insert(RoleTypeId, "typeId");
roles.insert(RoleSource, "source");
roles.insert(RoleLoggingEventType, "loggingEventType");
return roles;
}
bool LogsModel::live() const
{
return m_live;
}
void LogsModel::setLive(bool live)
{
if (m_live != live) {
m_live = live;
emit liveChanged();
}
}
QString LogsModel::deviceId() const
{
return m_deviceId;
@ -89,6 +122,11 @@ LogEntry *LogsModel::get(int index) const
return nullptr;
}
void LogsModel::notificationReceived(const QVariantMap &data)
{
qDebug() << "KLogModel notificatiion" << data;
}
void LogsModel::update()
{
m_busy = true;
@ -116,6 +154,7 @@ void LogsModel::update()
void LogsModel::logsReply(const QVariantMap &data)
{
qDebug() << "logs reply";
m_busy = false;
emit busyChanged();
beginResetModel();
@ -124,11 +163,43 @@ void LogsModel::logsReply(const QVariantMap &data)
QList<QVariant> logEntries = data.value("params").toMap().value("logEntries").toList();
foreach (const QVariant &logEntryVariant, logEntries) {
LogEntry *entry = new LogEntry(QDateTime::fromMSecsSinceEpoch(logEntryVariant.toMap().value("timestamp").toLongLong()), logEntryVariant.toMap().value("value"), this);
QVariantMap entryMap = logEntryVariant.toMap();
QDateTime timeStamp = QDateTime::fromMSecsSinceEpoch(entryMap.value("timestamp").toLongLong());
QString deviceId = entryMap.value("deviceId").toString();
QVariant value = entryMap.value("value");
QString typeId = entryMap.value("typeId").toString();
QMetaEnum sourceEnum = QMetaEnum::fromType<LogEntry::LoggingSource>();
LogEntry::LoggingSource loggingSource = (LogEntry::LoggingSource)sourceEnum.keyToValue(entryMap.value("source").toByteArray());
QMetaEnum loggingEventTypeEnum = QMetaEnum::fromType<LogEntry::LoggingEventType>();
LogEntry::LoggingEventType loggingEventType = (LogEntry::LoggingEventType)loggingEventTypeEnum.keyToValue(entryMap.value("eventType").toByteArray());
LogEntry *entry = new LogEntry(timeStamp, value, deviceId, typeId, loggingSource, loggingEventType, this);
m_list.append(entry);
qDebug() << "Added log entry" << entry->dayString() << QDateTime::fromMSecsSinceEpoch(logEntryVariant.toMap().value("timestamp").toLongLong()).date().dayOfWeek();
qDebug() << "Added log entry" << entry->dayString() << entry->value() << entry->deviceId() << entryMap << loggingEventType;
}
endResetModel();
emit countChanged();
}
void LogsModel::newLogEntryReceived(const QVariantMap &data)
{
if (!m_live) {
return;
}
beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
QVariantMap entryMap = data;
QDateTime timeStamp = QDateTime::fromMSecsSinceEpoch(entryMap.value("timestamp").toLongLong());
QString deviceId = entryMap.value("deviceId").toString();
QVariant value = entryMap.value("value");
QString typeId = entryMap.value("typeId").toString();
QMetaEnum sourceEnum = QMetaEnum::fromType<LogEntry::LoggingSource>();
LogEntry::LoggingSource loggingSource = (LogEntry::LoggingSource)sourceEnum.keyToValue(entryMap.value("source").toByteArray());
QMetaEnum loggingEventTypeEnum = QMetaEnum::fromType<LogEntry::LoggingEventType>();
LogEntry::LoggingEventType loggingEventType = (LogEntry::LoggingEventType)loggingEventTypeEnum.keyToValue(entryMap.value("eventType").toByteArray());
LogEntry *entry = new LogEntry(timeStamp, value, deviceId, typeId, loggingSource, loggingEventType, this);
m_list.append(entry);
qDebug() << "Added log entry" << entry->dayString() << entry->value() << entry->deviceId() << entryMap << loggingEventType;
endInsertRows();
emit countChanged();
}

View File

@ -3,6 +3,7 @@
#include <QAbstractListModel>
#include "jsonrpc/jsonhandler.h"
#include "types/logentry.h"
class LogsModel : public QAbstractListModel
@ -15,9 +16,16 @@ class LogsModel : public QAbstractListModel
Q_PROPERTY(QDateTime startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(QDateTime endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged)
public:
enum Roles {
RoleValue
RoleTimestamp,
RoleValue,
RoleDeviceId,
RoleTypeId,
RoleSource,
RoleLoggingEventType
};
explicit LogsModel(QObject *parent = nullptr);
@ -26,6 +34,9 @@ public:
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
bool live() const;
void setLive(bool live);
QString deviceId() const;
void setDeviceId(const QString &deviceId);
@ -40,8 +51,11 @@ public:
Q_INVOKABLE LogEntry* get(int index) const;
Q_INVOKABLE void notificationReceived(const QVariantMap &data);
signals:
void busyChanged();
void liveChanged();
void countChanged();
void deviceIdChanged();
void typeIdChanged();
@ -53,6 +67,7 @@ public slots:
private slots:
virtual void logsReply(const QVariantMap &data);
void newLogEntryReceived(const QVariantMap &data);
protected:
QList<LogEntry*> m_list;
@ -60,7 +75,9 @@ protected:
QString m_typeId;
QDateTime m_startTime = QDateTime::currentDateTime().addDays(-1);
QDateTime m_endTime = QDateTime::currentDateTime();
bool m_busy = false;
bool m_live = false;
};

View File

@ -4,6 +4,8 @@
#include "types/eventdescriptors.h"
#include "types/eventdescriptor.h"
#include "types/stateevaluator.h"
#include "types/ruleactions.h"
#include "types/ruleaction.h"
#include <QDebug>
@ -27,16 +29,16 @@ void RulesFilterModel::setRules(Rules *rules)
}
}
QUuid RulesFilterModel::filterEventDeviceId() const
QUuid RulesFilterModel::filterDeviceId() const
{
return m_filterEventDeviceId;
return m_filterDeviceId;
}
void RulesFilterModel::setFilterEventDeviceId(const QUuid &filterEventDeviceId)
void RulesFilterModel::setFilterDeviceId(const QUuid &filterDeviceId)
{
if (m_filterEventDeviceId != filterEventDeviceId) {
m_filterEventDeviceId = filterEventDeviceId;
emit filterEventDeviceIdChanged();
if (m_filterDeviceId != filterDeviceId) {
m_filterDeviceId = filterDeviceId;
emit filterDeviceIdChanged();
invalidateFilter();
}
}
@ -49,19 +51,29 @@ Rule *RulesFilterModel::get(int index) const
bool RulesFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
Q_UNUSED(source_parent)
if (!m_filterEventDeviceId.isNull()) {
bool found = true;
if (!m_filterDeviceId.isNull()) {
Rule* rule = m_rules->get(source_row);
bool found = false;
found = false;
for (int i = 0; i < rule->eventDescriptors()->rowCount(); i++) {
EventDescriptor *ed = rule->eventDescriptors()->get(i);
if (ed->deviceId() == m_filterEventDeviceId) {
if (ed->deviceId() == m_filterDeviceId) {
found = true;
break;
}
}
if (!found && !rule->stateEvaluator()->containsDevice(m_filterEventDeviceId)) {
return false;
if (!found && rule->stateEvaluator()->containsDevice(m_filterDeviceId)) {
found = true;
}
if (!found) {
for (int i = 0; i < rule->ruleActions()->rowCount(); i++) {
RuleAction *ra = rule->ruleActions()->get(i);
if (ra->deviceId() == m_filterDeviceId) {
found = true;
break;
}
}
}
}
return true;
return found;
}

View File

@ -11,7 +11,7 @@ class RulesFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(Rules* rules READ rules WRITE setRules NOTIFY rulesChanged)
Q_PROPERTY(QUuid filterEventDeviceId READ filterEventDeviceId WRITE setFilterEventDeviceId NOTIFY filterEventDeviceIdChanged)
Q_PROPERTY(QUuid filterDeviceId READ filterDeviceId WRITE setFilterDeviceId NOTIFY filterDeviceIdChanged)
public:
explicit RulesFilterModel(QObject *parent = nullptr);
@ -19,21 +19,21 @@ public:
Rules* rules() const;
void setRules(Rules* rules);
QUuid filterEventDeviceId() const;
void setFilterEventDeviceId(const QUuid &filterEventDeviceId);
QUuid filterDeviceId() const;
void setFilterDeviceId(const QUuid &filterDeviceId);
Q_INVOKABLE Rule* get(int index) const;
signals:
void rulesChanged();
void filterEventDeviceIdChanged();
void filterDeviceIdChanged();
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
Rules *m_rules = nullptr;
QUuid m_filterEventDeviceId;
QUuid m_filterDeviceId;
};
#endif // RULESFILTERMODEL_H

View File

@ -108,16 +108,15 @@ void ValueLogsProxyModel::logsReply(const QVariantMap &data)
if (counter > 0) {
avg = avg.toDouble() / counter;
} else if (entries[i-1].count() > 0) {
avg = entries[i-1].last();
avg = entries[i-1].last().toDouble();
} else if (m_list.count() > 0){
avg = m_list.last()->value();
avg = m_list.last()->value().toDouble();
} else {
continue;
}
LogEntry *entry = new LogEntry(startTime().addSecs(stepSize * i)/*.addSecs(stepSize * .5)*/, avg, this);
LogEntry *entry = new LogEntry(startTime().addSecs(stepSize * i)/*.addSecs(stepSize * .5)*/, avg, m_deviceId, QString(), LogEntry::LoggingSourceStates, LogEntry::LoggingEventTypeTrigger, this);
m_list.append(entry);
// qDebug() << "**" << m_minimumValue << entry->value();
if (m_minimumValue.isNull() || entry->value() < m_minimumValue) {
m_minimumValue = qRound(entry->value().toDouble());
}
@ -130,6 +129,13 @@ void ValueLogsProxyModel::logsReply(const QVariantMap &data)
endResetModel();
if (m_minimumValue.isNull()) {
m_minimumValue = 0;
}
if (m_maximumValue.isNull()) {
m_maximumValue = 0;
}
emit minimumValueChanged();
emit maximumValueChanged();
emit countChanged();

View File

@ -118,5 +118,8 @@
<file>ui/magic/SelectEventDescriptorPage.qml</file>
<file>ui/paramdelegates/DoubleParamDelegate.qml</file>
<file>ui/paramdescriptordelegates/ParamDescriptorDelegateBase.qml</file>
<file>ui/system/LogViewerPage.qml</file>
<file>ui/images/next.svg</file>
<file>ui/images/go-down.svg</file>
</qresource>
</RCC>

View File

@ -49,13 +49,13 @@ Rule *RuleManager::createNewRule()
void RuleManager::addRule(const QVariantMap params)
{
m_jsonClient->sendCommand("Rules.AddRule", params, this, "addRuleReply");
m_jsonClient->sendCommand("Rules.AddRule", params, this, "onAddRuleReply");
}
void RuleManager::addRule(Rule *rule)
{
QVariantMap params = JsonTypes::packRule(rule);
m_jsonClient->sendCommand("Rules.AddRule", params, this, "addRuleReply");
m_jsonClient->sendCommand("Rules.AddRule", params, this, "onAddRuleReply");
}
void RuleManager::removeRule(const QUuid &ruleId)
@ -80,9 +80,11 @@ void RuleManager::handleRulesNotification(const QVariantMap &params)
QUuid ruleId = ruleMap.value("id").toUuid();
QString name = ruleMap.value("name").toString();
bool enabled = ruleMap.value("enabled").toBool();
bool active = ruleMap.value("active").toBool();
Rule* rule = new Rule(ruleId, m_rules);
rule->setName(name);
rule->setEnabled(enabled);
rule->setActive(active);
parseEventDescriptors(ruleMap.value("eventDescriptors").toList(), rule);
StateEvaluator* stateEvaluator = parseStateEvaluator(ruleMap.value("stateEvaluator").toMap());
stateEvaluator->setParent(rule);
@ -131,9 +133,10 @@ void RuleManager::getRuleDetailsReply(const QVariantMap &params)
parseStateEvaluator(ruleMap.value("stateEvaluator").toMap());
}
void RuleManager::addRuleReply(const QVariantMap &params)
void RuleManager::onAddRuleReply(const QVariantMap &params)
{
qDebug() << "Add rule reply" << params;
emit addRuleReply(params.value("params").toMap().value("ruleError").toString());
}
void RuleManager::removeRuleReply(const QVariantMap &params)

View File

@ -35,7 +35,7 @@ private slots:
void handleRulesNotification(const QVariantMap &params);
void getRulesReply(const QVariantMap &params);
void getRuleDetailsReply(const QVariantMap &params);
void addRuleReply(const QVariantMap &params);
void onAddRuleReply(const QVariantMap &params);
void removeRuleReply(const QVariantMap &params);
void onEditRuleReply(const QVariantMap &params);
@ -45,6 +45,7 @@ private:
void parseRuleActions(const QVariantList &ruleActions, Rule *rule);
signals:
void addRuleReply(const QString &ruleError);
void editRuleReply(const QString &ruleError);
private:

View File

@ -26,6 +26,9 @@ Page {
certDialog.fingerprint = fingerprint
certDialog.open();
}
onConnectionError: {
pageStack.pop(root)
}
}
ColumnLayout {

View File

@ -1,5 +1,6 @@
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import "components"
import Guh 1.0
@ -22,7 +23,14 @@ Page {
Connections {
target: Engine.ruleManager
onAddRuleReply: {
if (ruleError == "RuleErrorNoError") {
pageStack.pop();
}
}
onEditRuleReply: {
print("have add rule reply")
if (ruleError == "RuleErrorNoError") {
pageStack.pop();
}
@ -36,7 +44,21 @@ Page {
delegate: SwipeDelegate {
id: ruleDelegate
width: parent.width
text: model.name
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
height: app.iconSize
width: height
name: "../images/magic.svg"
color: !model.enabled ? "gray" : (model.active ? "red" : app.guhAccent)
}
Label {
Layout.fillWidth: true
text: model.name
}
}
onClicked: {
var editRulePage = pageStack.push(Qt.resolvedUrl("magic/EditRulePage.qml"), {rule: Engine.ruleManager.rules.get(index) })

View File

@ -56,11 +56,12 @@ Page {
id: swipeView
Layout.fillWidth: true
Layout.fillHeight: true
currentIndex: pageIndicator.currentIndex
DevicesPage {
width: parent.view.width
height: parent.view.height
shownInterfaces: ["light", "weather", "sensor", "media"]
shownInterfaces: ["light", "weather", "sensor", "media", "garagegate"]
}
DevicesPage {
@ -97,13 +98,11 @@ Page {
}
PageIndicator {
id: pageIndicator
Layout.alignment: Qt.AlignHCenter
count: swipeView.count
currentIndex: swipeView.currentIndex
interactive: true
}
}
}

View File

@ -344,7 +344,7 @@ Page {
case 1:
case 2:
case 3:
Engine.deviceManager.pairDevice(d.deviceClass.id, d.deviceDescriptorId);
Engine.deviceManager.pairDevice(d.deviceClass.id, d.deviceDescriptorId, nameTextField.text);
break;
}

View File

@ -15,30 +15,52 @@ Page {
ColumnLayout {
anchors.fill: parent
anchors.margins: app.margins
Label {
Layout.fillWidth: true
text: "Connected to:"
color: Material.accent
}
RowLayout {
ColumnLayout {
Layout.fillWidth: true
Layout.margins: app.margins
Label {
Layout.fillWidth: true
text: Engine.connection.url
text: "Connected to:"
color: Material.accent
}
Button {
text: "Disconnect"
onClicked: {
settings.lastConnectedHost = "";
Engine.connection.disconnect();
RowLayout {
Layout.fillWidth: true
Label {
Layout.fillWidth: true
text: Engine.connection.url
}
Button {
text: "Disconnect"
onClicked: {
settings.lastConnectedHost = "";
Engine.connection.disconnect();
}
}
}
}
ThinDivider {}
ItemDelegate {
Layout.fillWidth: true
contentItem: RowLayout {
Label {
text: "Log viewer"
Layout.fillWidth: true
}
Image {
source: "images/next.svg"
Layout.preferredHeight: parent.height
Layout.preferredWidth: height
}
}
onClicked: pageStack.push(Qt.resolvedUrl("system/LogViewerPage.qml"))
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true

View File

@ -40,11 +40,11 @@ Item {
property int minTemp: {
var lower = Math.floor(root.model.minimumValue - 2);
var upper = Math.ceil(root.model.maximumValue + 2);
print("upper", upper, "lower", lower)
if (!lower || !upper) {
if (lower == undefined || upper == undefined) {
return 0
}
print("upper", upper, "lower", lower)
while ((upper - lower) % 10 != 0) {
lower -= 1;
if ((upper - lower) % 10 != 0) {
@ -57,7 +57,7 @@ Item {
property int maxTemp: {
var lower = Math.floor(root.model.minimumValue - 2);
var upper = Math.ceil(root.model.maximumValue + 2);
if (!lower || !upper) {
if (lower == undefined || upper == undefined) {
return 0
}
while ((upper - lower) % 10 != 0) {

View File

@ -4,6 +4,7 @@ import QtQuick.Controls 2.1
ToolButton {
property alias imageSource: image.name
property alias color: image.color
property alias keyColor: image.keyColor
contentItem: Item {
height: 20

View File

@ -98,7 +98,7 @@ DevicePageBase {
property var actionType: deviceClass.actionTypes.get(index)
property var actionValue: device.hasState(actionType.id) ? device.states.getState(actionType.id).value : null
source: {
print("actiontyoe is", actionType.name, actionValue, actionType.paramTypes.count)
print("actiontype is", actionType.name, actionValue, actionType.paramTypes.count)
for (var i = 0; i < actionType.paramTypes.count; i++) {
print("have actionType param:", actionType.paramTypes.get(i).name, actionType.paramTypes.get(i).type)
}

View File

@ -3,6 +3,7 @@ import QtQuick.Controls 2.1
import QtQuick.Layouts 1.1
import Guh 1.0
import "../components"
import "../paramdelegates"
Page {
id: root
@ -14,28 +15,123 @@ Page {
text: "Details for " + root.device.name
onBackPressed: pageStack.pop()
}
ColumnLayout {
anchors { left: parent.left; top: parent.top; right: parent.right; margins: app.margins }
spacing: app.margins
Flickable {
anchors.fill: parent
contentHeight: statesColumn.height + app.margins*2
Repeater {
model: deviceClass.stateTypes
delegate: RowLayout {
width: parent.width
height: app.largeFont
ColumnLayout {
id: statesColumn
anchors { left: parent.left; top: parent.top; right: parent.right; margins: app.margins }
spacing: app.margins
Label {
id: stateLabel
Layout.preferredWidth: parent.width / 2
text: displayName
}
Repeater {
model: deviceClass.stateTypes
delegate: RowLayout {
width: parent.width
height: app.largeFont
Label {
id: valueLable
Layout.fillWidth: true
text: device.states.getState(id).value + " " + deviceClass.stateTypes.getStateType(id).unitString
property var stateType: deviceClass.stateTypes.get(index)
Label {
id: stateLabel
Layout.preferredWidth: parent.width / 2
text: displayName
}
Loader {
id: placeHolder
Layout.fillWidth: true
sourceComponent: {
var writable = deviceClass.actionTypes.getActionType(id) !== null;
if (!writable) {
return labelComponent;
}
switch (stateType.type) {
case "Bool":
return boolComponent;
case "Int":
case "Double":
if (stateType.minValue !== undefined && stateType.maxValue !== undefined) {
return sliderComponent;
}
return textFieldComponent;
case "String":
return textFieldComponent;
}
console.warn("DeviceStateDetailsPage: Type delegate not implemented", stateType.type)
return null;
}
}
Binding {
target: placeHolder.item
when: placeHolder.item
property: "value"
value: device.states.getState(id).value
}
// Binding {
// target: placeHolder.item
// when: placeHolder.item
// property: "enabled"
// value: deviceClass.actionTypes.getActionType(id) !== null
// }
Binding {
target: placeHolder.item
when: placeHolder.item
property: "stateTypeId"
value: id
}
// Label {
// id: valueLable
// Layout.fillWidth: true
// text: device.states.getState(id).value + " " + deviceClass.stateTypes.getStateType(id).unitString
// }
}
}
}
}
Component {
id: labelComponent
Label {
property var value: ""
property var stateTypeId: null
text: value + " " + deviceClass.stateTypes.getStateType(stateTypeId).unitString
horizontalAlignment: Text.AlignHCenter
}
}
Component {
id: textFieldComponent
TextField {
property var value: ""
property var stateTypeId: null
text: value
onEditingFinished: {
executeAction(stateTypeId, text)
}
}
}
Component {
id: boolComponent
Switch {
property var value: false
property var stateTypeId: null
checked: value
onClicked: executeAction(stateTypeId, checked)
}
}
function executeAction(stateTypeId, value) {
var paramList = []
var muteParam = {}
muteParam["paramTypeId"] = stateTypeId;
muteParam["value"] = value;
paramList.push(muteParam)
Engine.deviceManager.executeAction(root.device.id, stateTypeId, paramList)
}
}

View File

@ -0,0 +1,178 @@
<?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.91+devel r"
viewBox="0 0 96 96.000001"
sodipodi:docname="go-down.svg">
<defs
id="defs4876">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath16">
<path
d="m 0,595.28 841.89,0 L 841.89,0 0,0 0,595.28 Z"
id="path18"
inkscape:connector-curvature="0" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.5111573"
inkscape:cx="-151.39141"
inkscape:cy="28.091447"
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:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076" />
<sodipodi:guide
orientation="1,0"
position="84,-8.0000001"
id="guide4080" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172" />
<sodipodi:guide
position="92,-8.0000001"
orientation="1,0"
id="guide4760" />
<sodipodi:guide
position="104,12"
orientation="0,1"
id="guide4207" />
</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)">
<g
transform="matrix(0,-1,-1,0,106.64791,106.6479)"
id="g5743"
inkscape:label="Layer 1">
<g
style="display:inline"
id="g5745"
transform="matrix(0,-1,-1,0,373.50506,516.50504)">
<g
inkscape:label="Layer 1"
id="g5747"
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
inkscape:export-filename="next01.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<g
style="display:inline"
id="g5749"
transform="matrix(-1,0,0,1,575.99999,611)">
<path
inkscape:connector-curvature="0"
id="path5751"
d="m 437.00209,379.49587 c -5.24691,4.1449 -10.54573,8.128 -15.94494,11.7914 -6.00476,4.0757 -11.8322,7.7257 -17.4773,10.9536 -5.20923,2.9783 -9.64507,5.2206 -13.59532,7.0003 -3.95086,-1.7798 -8.3889,-4.0213 -13.59947,-7.0003 -5.64495,-3.2278 -11.47152,-6.8778 -17.47731,-10.9536 -5.39821,-3.6627 -10.69595,-7.6466 -15.94285,-11.7914 l 7.93911,0 c 3.15098,2.4002 6.38063,4.7681 9.75501,7.0752 4.26477,2.9172 8.6291,5.6557 13.09079,8.2194 4.52823,2.6011 10.75085,5.7064 15.00884,7.7151 l 0.002,0 1.22173,0.5752 1.22172,-0.5752 0.002,0 c 4.25899,-2.0087 10.48126,-5.1138 15.00884,-7.7151 4.46175,-2.5637 8.82538,-5.3023 13.09078,-8.2194 3.37342,-2.307 6.60393,-4.6749 9.75502,-7.0752 l 7.9412,0 z"
style="display:inline;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1" />
<rect
transform="scale(-1,1)"
y="345.36221"
x="-438.00244"
height="96"
width="96.037987"
id="rect5753"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate" />
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -0,0 +1,178 @@
<?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.91+devel r"
viewBox="0 0 96 96.000001"
sodipodi:docname="go-next.svg">
<defs
id="defs4876">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath16">
<path
d="m 0,595.28 841.89,0 L 841.89,0 0,0 0,595.28 Z"
id="path18"
inkscape:connector-curvature="0" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.8889466"
inkscape:cx="-134.66566"
inkscape:cy="89.679926"
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:grid
type="xygrid"
id="grid5451"
empspacing="8" />
<sodipodi:guide
orientation="1,0"
position="8,-8.0000001"
id="guide4063" />
<sodipodi:guide
orientation="1,0"
position="4,-8.0000001"
id="guide4065" />
<sodipodi:guide
orientation="0,1"
position="-8,88.000001"
id="guide4067" />
<sodipodi:guide
orientation="0,1"
position="-8,92.000001"
id="guide4069" />
<sodipodi:guide
orientation="0,1"
position="104,4"
id="guide4071" />
<sodipodi:guide
orientation="0,1"
position="-5,8.0000001"
id="guide4073" />
<sodipodi:guide
orientation="1,0"
position="88,-8.0000001"
id="guide4077" />
<sodipodi:guide
orientation="0,1"
position="-8,84.000001"
id="guide4074" />
<sodipodi:guide
orientation="1,0"
position="12,-8.0000001"
id="guide4076" />
<sodipodi:guide
orientation="1,0"
position="84,-8.0000001"
id="guide4080" />
<sodipodi:guide
position="48,-8.0000001"
orientation="1,0"
id="guide4170" />
<sodipodi:guide
position="-8,48"
orientation="0,1"
id="guide4172" />
<sodipodi:guide
position="92,-8.0000001"
orientation="1,0"
id="guide4760" />
<sodipodi:guide
position="104,12"
orientation="0,1"
id="guide4207" />
</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)">
<g
inkscape:label="Layer 1"
id="layer1-695"
transform="matrix(-1,0,0,1,-39.714289,-1.4294891e-5)">
<g
transform="matrix(0,-1,-1,0,373.50506,516.50504)"
id="g4845-27"
style="display:inline">
<g
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
inkscape:export-filename="next01.png"
transform="matrix(-0.9996045,0,0,1,575.94296,-611.00001)"
id="g4778-56"
inkscape:label="Layer 1">
<g
transform="matrix(-1,0,0,1,575.99999,611)"
id="g4780-75"
style="display:inline">
<path
style="display:inline;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1"
d="m 437.00209,379.49587 c -5.24691,4.1449 -10.54573,8.128 -15.94494,11.7914 -6.00476,4.0757 -11.8322,7.7257 -17.4773,10.9536 -5.20923,2.9783 -9.64507,5.2206 -13.59532,7.0003 -3.95086,-1.7798 -8.3889,-4.0213 -13.59947,-7.0003 -5.64495,-3.2278 -11.47152,-6.8778 -17.47731,-10.9536 -5.39821,-3.6627 -10.69595,-7.6466 -15.94285,-11.7914 l 7.93911,0 c 3.15098,2.4002 6.38063,4.7681 9.75501,7.0752 4.26477,2.9172 8.6291,5.6557 13.09079,8.2194 4.52823,2.6011 10.75085,5.7064 15.00884,7.7151 l 0.002,0 1.22173,0.5752 1.22172,-0.5752 0.002,0 c 4.25899,-2.0087 10.48126,-5.1138 15.00884,-7.7151 4.46175,-2.5637 8.82538,-5.3023 13.09078,-8.2194 3.37342,-2.307 6.60393,-4.6749 9.75502,-7.0752 l 7.9412,0 z"
id="path4265-0-4"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:none;stroke:none;stroke-width:4;marker:none;enable-background:accumulate"
id="rect4782-01"
width="96.037987"
height="96"
x="-438.00244"
y="345.36221"
transform="scale(-1,1)" />
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,5 +1,6 @@
import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import "../components"
import Guh 1.0
@ -20,7 +21,32 @@ Page {
}
function addRule() {
pageStack.push(Qt.resolvedUrl("NewThingMagicPage.qml"), {device: root.device, text: "Add magic"})
// pageStack.push(Qt.resolvedUrl("NewThingMagicPage.qml"), {device: root.device, text: "Add magic"})
var rule = Engine.ruleManager.createNewRule();
var page = pageStack.push(Qt.resolvedUrl("EditRulePage.qml"), {rule: rule});
var eventDescriptor = rule.eventDescriptors.createNewEventDescriptor();
eventDescriptor.deviceId = device.id;
page.selectEventDescriptorData(eventDescriptor);
page.onAccept.connect(function() {
Engine.ruleManager.addRule(page.rule);
})
}
Connections {
target: Engine.ruleManager
onAddRuleReply: {
if (ruleError == "RuleErrorNoError") {
pageStack.pop();
}
}
onEditRuleReply: {
print("have add rule reply")
if (ruleError == "RuleErrorNoError") {
pageStack.pop();
}
}
}
ListView {
@ -30,11 +56,46 @@ Page {
model: RulesFilterModel {
id: rulesFilterModel
rules: Engine.ruleManager.rules
filterEventDeviceId: root.device.id
filterDeviceId: root.device.id
}
delegate: ItemDelegate {
text: model.name
delegate: SwipeDelegate {
width: parent.width
contentItem: RowLayout {
spacing: app.margins
ColorIcon {
height: app.iconSize
width: height
name: "../images/magic.svg"
color: !model.enabled ? "gray" : (model.active ? "red" : app.guhAccent)
}
Label {
Layout.fillWidth: true
text: model.name
}
}
onClicked: {
var editRulePage = pageStack.push(Qt.resolvedUrl("EditRulePage.qml"), {rule: rulesFilterModel.get(index) })
editRulePage.onAccept.connect(function() {
Engine.ruleManager.editRule(editRulePage.rule);
})
}
swipe.right: Item {
height: ruleDelegate.height
width: height
anchors.right: parent.right
ColorIcon {
anchors.fill: parent
anchors.margins: app.margins
name: "../images/delete.svg"
color: "red"
}
SwipeDelegate.onClicked: Engine.ruleManager.removeRule(model.id)
}
}
}

View File

@ -23,7 +23,7 @@ Page {
id: header
onBackPressed: root.backPressed();
property bool interfacesMode: true
property bool interfacesMode: false
onInterfacesModeChanged: root.buildInterface()
HeaderButton {
@ -79,6 +79,7 @@ Page {
model: actualModel
delegate: ItemDelegate {
width: parent.width
text: model.text
onClicked: {
if (header.interfacesMode) {

View File

@ -23,7 +23,7 @@ Page {
id: header
onBackPressed: root.backPressed();
property bool interfacesMode: true
property bool interfacesMode: false
onInterfacesModeChanged: root.buildInterface()
HeaderButton {
@ -88,6 +88,7 @@ Page {
delegate: ItemDelegate {
text: model.text
width: parent.width
onClicked: {
if (header.interfacesMode) {
if (root.device) {

View File

@ -27,6 +27,7 @@ Page {
id: delegateRepeater
model: root.actionType.paramTypes
delegate: ParamDelegate {
width: parent.width
paramType: root.actionType.paramTypes.get(index)
value: paramType.defaultValue

View File

@ -35,14 +35,17 @@ Page {
id: supportedInterfacesModel
ListElement { interfaceName: "battery"; name: "Battery powered devices" }
ListElement { interfaceName: "temperatureSensor"; name: "Temperature sensors" }
ListElement { interfaceName: "light"; name: "Lights" }
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: thingButton.checked ? Engine.deviceManager.devices : supportedInterfacesModel
clip: true
delegate: ItemDelegate {
text: model.name
width: parent.width
onClicked: {
if (thingButton.checked) {
root.thingSelected(Engine.deviceManager.devices.get(index))

View File

@ -5,13 +5,15 @@ import Guh 1.0
ItemDelegate {
id: root
height: layout.height
property var paramType: null
property var value: null
property int operatorType: ParamDescriptors.ValueOperatorEquals
RowLayout {
anchors.fill: parent
id: layout
anchors { left: parent.left; top: parent.top; right: parent.right}
anchors.margins: app.margins
spacing: app.margins
Label {
@ -42,13 +44,14 @@ ItemDelegate {
case "is smaller":
root.operatorType = ParamDescriptor.ValueOperatorLess;
break;
case "is greater of equal":
case "is greater or equal":
root.operatorType = ParamDescriptor.ValueOperatorGreaterOrEqual;
break;
case "is smaller or equal":
root.operatorType = ParamDescriptor.ValueOperatorLessOrEqual;
break;
}
print("set operator to", root.operatorType, currentText)
}
}
@ -78,6 +81,9 @@ ItemDelegate {
id: textFieldComponent
TextField {
text: ""
onTextChanged: {
root.value = text;
}
}
}

View File

@ -0,0 +1,166 @@
import QtQuick 2.8
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
import Guh 1.0
import "../components"
Page {
id: root
header: GuhHeader {
text: "Log viewer"
onBackPressed: pageStack.pop()
HeaderButton {
imageSource: "../images/go-down.svg"
color: root.autoScroll ? app.guhAccent : keyColor
onClicked: root.autoScroll = !root.autoScroll
}
}
property bool autoScroll: true
LogsModel {
id: logsModel
startTime: {
var date = new Date();
date.setHours(new Date().getHours() - 1);
return date;
}
endTime: new Date()
live: true
Component.onCompleted: update()
onCountChanged: {
if (root.autoScroll) {
listView.positionViewAtEnd()
}
}
}
BusyIndicator {
anchors.centerIn: listView
visible: logsModel.busy
}
ListView {
id: listView
model: logsModel
anchors.fill: parent
clip: true
headerPositioning: ListView.OverlayHeader
property int column0Width: root.width / 10 * 3
property int column1Width: root.width / 10 * 1
property int column2Width: root.width / 10 * 3
property int column3Width: root.width / 10 * 2
property int column4Width: root.width / 10 * 1
header: Rectangle {
width: parent.width
height: app.margins * 3
color: "white"
z: 2
Row {
width: parent.width
anchors.verticalCenter: parent.verticalCenter
Label {
width: listView.column0Width
text: "Time"
}
Label {
text: "Type"
width: listView.column1Width
}
Label {
width: listView.column2Width
text: "Device"
}
Label {
width: listView.column3Width
text: "Object"
}
Label {
width: listView.column4Width
text: "Value"
}
}
ThinDivider {
anchors { left: parent.left; bottom: parent.bottom; right: parent.right }
}
}
delegate: Row {
id: delegate
property var device: Engine.deviceManager.devices.getDevice(model.deviceId)
property var deviceClass: device ? Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) : null
Label {
width: listView.column0Width
text: Qt.formatDateTime(model.timestamp,"dd.MM.yy - hh:mm:ss")
elide: Text.ElideRight
}
Label {
width: listView.column1Width
text: {
switch (model.source) {
case LogEntry.LoggingSourceStates:
return "SC";
case LogEntry.LoggingSourceSystem:
return "SYS";
case LogEntry.LoggingSourceActions:
return "AE";
case LogEntry.LoggingSourceEvents:
return "E";
case LogEntry.LoggingSourceRules:
return "R";
}
// switch (model.loggingEventType) {
// case LogEntry.LoggingEventTypeTrigger:
// return "T";
// case LogEntry.LoggingEventTypeExitActionsExecuted:
// return "A";
// case LogEntry.LoggingEventTypeActiveChange:
// return "R";
// case LogEntry.LoggingEventTypeExitActionsExecuted:
// return "EA";
// case LogEntry.LoggingEventTypeEnabledChange:
// return "E";
// }
return "N/A";
}
}
Label {
width: listView.column2Width
text: delegate.device.name
elide: Text.ElideRight
}
Label {
width: listView.column3Width
text : {
switch (model.source) {
case LogEntry.LoggingSourceStates:
return delegate.deviceClass.stateTypes.getStateType(model.typeId).displayName;
case LogEntry.LoggingSourceSystem:
return "SYS";
case LogEntry.LoggingSourceActions:
return delegate.deviceClass.actionTypes.getActionType(model.typeId).displayName;
case LogEntry.LoggingSourceEvents:
return delegate.deviceClass.eventTypes.getEventType(model.typeId).displayName;
case LogEntry.LoggingSourceRules:
return Engine.ruleManager.rules.getRule(model.typeId).name;
}
return "N/A";
}
elide: Text.ElideRight
}
Label {
width: listView.column4Width
text: model.value
elide: Text.ElideRight
}
}
}
}

View File

@ -2,10 +2,14 @@
#include <QDateTime>
LogEntry::LogEntry(const QDateTime &timestamp, const QVariant &value, QObject *parent):
LogEntry::LogEntry(const QDateTime &timestamp, const QVariant &value, const QString &deviceId, const QString &typeId, LoggingSource source, LoggingEventType loggingEventType, QObject *parent):
QObject(parent),
m_value(value),
m_timeStamp(timestamp)
m_timeStamp(timestamp),
m_deviceId(deviceId),
m_typeId(typeId),
m_source(source),
m_loggingEventType(loggingEventType)
{
}
@ -20,6 +24,26 @@ QDateTime LogEntry::timestamp() const
return m_timeStamp;
}
QString LogEntry::deviceId() const
{
return m_deviceId;
}
QString LogEntry::typeId() const
{
return m_typeId;
}
LogEntry::LoggingSource LogEntry::source() const
{
return m_source;
}
LogEntry::LoggingEventType LogEntry::loggingEventType() const
{
return m_loggingEventType;
}
QString LogEntry::timeString() const
{
return m_timeStamp.time().toString("hh:mm");

View File

@ -9,6 +9,10 @@ class LogEntry : public QObject
{
Q_OBJECT
Q_PROPERTY(QVariant value READ value CONSTANT)
Q_PROPERTY(QString deviceId READ deviceId CONSTANT)
Q_PROPERTY(QString typeId READ typeId CONSTANT)
Q_PROPERTY(LoggingSource source READ source CONSTANT)
Q_PROPERTY(LoggingEventType loggingEventType READ loggingEventType CONSTANT)
Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT)
Q_PROPERTY(QString timeString READ timeString CONSTANT)
@ -16,10 +20,33 @@ class LogEntry : public QObject
Q_PROPERTY(QString dateString READ dateString CONSTANT)
public:
explicit LogEntry(const QDateTime &timestamp, const QVariant &value, QObject *parent = nullptr);
enum LoggingSource {
LoggingSourceSystem,
LoggingSourceEvents,
LoggingSourceActions,
LoggingSourceStates,
LoggingSourceRules
};
Q_ENUM(LoggingSource)
Q_DECLARE_FLAGS(LoggingSources, LoggingSource)
enum LoggingEventType {
LoggingEventTypeTrigger,
LoggingEventTypeActiveChange,
LoggingEventTypeEnabledChange,
LoggingEventTypeActionsExecuted,
LoggingEventTypeExitActionsExecuted
};
Q_ENUM(LoggingEventType)
explicit LogEntry(const QDateTime &timestamp, const QVariant &value, const QString &deviceId = QString(), const QString &typeId = QString(), LoggingSource source = LoggingSourceSystem, LoggingEventType loggingEventType = LoggingEventTypeTrigger, QObject *parent = nullptr);
QVariant value() const;
QDateTime timestamp() const;
QString deviceId() const;
QString typeId() const;
LoggingSource source() const;
LoggingEventType loggingEventType() const;
QString timeString() const;
QString dayString() const;
@ -28,6 +55,10 @@ public:
private:
QVariant m_value;
QDateTime m_timeStamp;
QString m_deviceId;
QString m_typeId;
LoggingSource m_source;
LoggingEventType m_loggingEventType;
};
#endif // LOGENTRY_H

View File

@ -48,6 +48,19 @@ void Rule::setEnabled(bool enabled)
}
}
bool Rule::active() const
{
return m_active;
}
void Rule::setActive(bool active)
{
if (m_active != active) {
m_active = active;
emit activeChanged();
}
}
EventDescriptors *Rule::eventDescriptors() const
{
return m_eventDescriptors;

View File

@ -14,6 +14,7 @@ class Rule : public QObject
Q_PROPERTY(QUuid id READ id CONSTANT)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
Q_PROPERTY(bool active READ active NOTIFY activeChanged)
Q_PROPERTY(EventDescriptors* eventDescriptors READ eventDescriptors CONSTANT)
Q_PROPERTY(StateEvaluator* stateEvaluator READ stateEvaluator CONSTANT)
Q_PROPERTY(RuleActions* ruleActions READ ruleActions CONSTANT)
@ -28,6 +29,9 @@ public:
bool enabled() const;
void setEnabled(bool enabled);
bool active() const;
void setActive(bool active);
EventDescriptors* eventDescriptors() const;
StateEvaluator *stateEvaluator() const;
RuleActions* ruleActions() const;
@ -37,11 +41,13 @@ public:
signals:
void nameChanged();
void enabledChanged();
void activeChanged();
private:
QUuid m_id;
QString m_name;
bool m_enabled = false;
bool m_active = false;
EventDescriptors *m_eventDescriptors = nullptr;
StateEvaluator *m_stateEvaluator = nullptr;
RuleActions *m_ruleActions = nullptr;

View File

@ -25,6 +25,10 @@ QVariant Rules::data(const QModelIndex &index, int role) const
return m_list.at(index.row())->name();
case RoleId:
return m_list.at(index.row())->id();
case RoleEnabled:
return m_list.at(index.row())->enabled();
case RoleActive:
return m_list.at(index.row())->active();
}
return QVariant();
}
@ -34,6 +38,8 @@ QHash<int, QByteArray> Rules::roleNames() const
QHash<int, QByteArray> roles;
roles.insert(RoleName, "name");
roles.insert(RoleId, "id");
roles.insert(RoleEnabled, "enabled");
roles.insert(RoleActive, "active");
return roles;
}

View File

@ -11,7 +11,9 @@ class Rules : public QAbstractListModel
public:
enum Roles {
RoleName,
RoleId
RoleId,
RoleEnabled,
RoleActive
};
explicit Rules(QObject *parent = nullptr);