From 834c7a43c5abd1326aadd599af8ca9ca608a3804 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Fri, 10 Nov 2017 15:11:49 +0100 Subject: [PATCH] moar work! --- guh-control/devicemanager.cpp | 33 +- guh-control/devicemanager.h | 1 + guh-control/jsonrpc/jsontypes.cpp | 3 +- guh-control/main.cpp | 4 +- guh-control/models/valuelogsproxymodel.cpp | 15 +- guh-control/resources.qrc | 6 + guh-control/ui/EditDevicesPage.qml | 38 +++ guh-control/ui/MagicPage.qml | 4 +- guh-control/ui/MainPage.qml | 11 +- guh-control/ui/NewDeviceWizard.qml | 2 +- guh-control/ui/components/GuhHeader.qml | 1 + guh-control/ui/components/HeaderButton.qml | 7 +- guh-control/ui/customviews/SensorView.qml | 83 ++++- .../devicelistpages/LightsDeviceListPage.qml | 4 +- .../ui/devicepages/ColorLightDevicePage.qml | 28 +- .../ui/devicepages/ConfigureThingPage.qml | 53 ++++ guh-control/ui/devicepages/DevicePageBase.qml | 15 + .../ui/devicepages/GenericDevicePage.qml | 10 - .../ui/devicepages/WeatherDevicePage.qml | 43 +-- guh-control/ui/images/magic.svg | 296 ++++++++++++++++++ guh-control/ui/magic/DeviceRulesPage.qml | 75 +++++ guh-control/ui/magic/NewRulePage.qml | 3 + guh-control/ui/magic/NewThingMagicPage.qml | 75 +++++ guh-control/ui/magic/SelectActionPage.qml | 56 +++- guh-control/ui/magic/SelectEventPage.qml | 46 +++ guh-control/ui/main.qml | 10 + libguh-common/types/logentry.h | 1 + libguh-common/types/param.cpp | 14 +- libguh-common/types/param.h | 12 +- libguh-common/types/params.cpp | 14 +- libguh-common/types/params.h | 8 +- libguh-common/types/paramtype.cpp | 10 + libguh-common/types/paramtype.h | 5 + libguh-common/types/paramtypes.cpp | 4 +- libguh-common/types/paramtypes.h | 2 +- 35 files changed, 876 insertions(+), 116 deletions(-) create mode 100644 guh-control/ui/EditDevicesPage.qml create mode 100644 guh-control/ui/devicepages/ConfigureThingPage.qml create mode 100644 guh-control/ui/images/magic.svg create mode 100644 guh-control/ui/magic/DeviceRulesPage.qml create mode 100644 guh-control/ui/magic/NewThingMagicPage.qml create mode 100644 guh-control/ui/magic/SelectEventPage.qml diff --git a/guh-control/devicemanager.cpp b/guh-control/devicemanager.cpp index cc38238b..5061897d 100644 --- a/guh-control/devicemanager.cpp +++ b/guh-control/devicemanager.cpp @@ -83,7 +83,8 @@ void DeviceManager::addDevice(const QUuid &deviceClassId, const QString &name, c void DeviceManager::notificationReceived(const QVariantMap &data) { - if (data.value("notification").toString() == "Devices.StateChanged") { + QString notification = data.value("notification").toString(); + if (notification == "Devices.StateChanged") { qDebug() << "Device state changed" << data.value("params"); Device *dev = m_devices->getDevice(data.value("params").toMap().value("deviceId").toUuid()); if (!dev) { @@ -91,20 +92,33 @@ void DeviceManager::notificationReceived(const QVariantMap &data) return; } dev->setStateValue(data.value("params").toMap().value("stateTypeId").toUuid(), data.value("params").toMap().value("value")); + } else if (notification == "Devices.DeviceAdded") { + Device *dev = JsonTypes::unpackDevice(data.value("params").toMap().value("device").toMap(), m_devices); + if (!dev) { + qWarning() << "Cannot parse json device:" << data; + return; + } + m_devices->addDevice(dev); + } else if (notification == "Devices.DeviceRemoved") { + QUuid deviceId = data.value("params").toMap().value("deviceId").toUuid(); + qDebug() << "JsonRpc: Notification: Device removed" << deviceId.toString(); + Device *device = m_devices->getDevice(deviceId); + m_devices->removeDevice(device); + device->deleteLater(); } else { - qWarning() << "DeviceManager unhandled device notification received" << data; + qWarning() << "DeviceManager unhandled device notification received" << notification; } } void DeviceManager::getVendorsResponse(const QVariantMap ¶ms) { - qDebug() << "Got GetSupportedVendors response" << params; +// qDebug() << "Got GetSupportedVendors response" << params; if (params.value("params").toMap().keys().contains("vendors")) { QVariantList vendorList = params.value("params").toMap().value("vendors").toList(); foreach (QVariant vendorVariant, vendorList) { Vendor *vendor = JsonTypes::unpackVendor(vendorVariant.toMap(), Engine::instance()->deviceManager()->vendors()); m_vendors->addVendor(vendor); - qDebug() << "Added Vendor:" << vendor->name(); +// qDebug() << "Added Vendor:" << vendor->name(); } } @@ -166,6 +180,10 @@ void DeviceManager::getConfiguredDevicesResponse(const QVariantMap ¶ms) QVariant value = stateMap.toMap().value("value"); if (st->type() == "Bool") { value.convert(QVariant::Bool); + } else if (st->type() == "Double") { + value.convert(QVariant::Double); + } else if (st->type() == "Int") { + value.convert(QVariant::Int); } device->setStateValue(stateTypeId, value); } @@ -189,11 +207,8 @@ void DeviceManager::addDeviceResponse(const QVariantMap ¶ms) void DeviceManager::removeDeviceResponse(const QVariantMap ¶ms) { - QUuid deviceId = params.value("params").toMap().value("deviceId").toUuid(); - qDebug() << "JsonRpc: Notification: Device removed" << deviceId.toString(); - Device *device = m_devices->getDevice(deviceId); - m_devices->removeDevice(device); - device->deleteLater(); + qDebug() << "Device removed response" << params; + emit removeDeviceReply(params.value("params").toMap()); } void DeviceManager::pairDeviceResponse(const QVariantMap ¶ms) diff --git a/guh-control/devicemanager.h b/guh-control/devicemanager.h index 081d1d0f..d886d4fe 100644 --- a/guh-control/devicemanager.h +++ b/guh-control/devicemanager.h @@ -73,6 +73,7 @@ signals: void pairDeviceReply(const QVariantMap ¶ms); void confirmPairingReply(const QVariantMap ¶ms); void addDeviceReply(const QVariantMap ¶ms); + void removeDeviceReply(const QVariantMap ¶ms); private: Vendors *m_vendors; diff --git a/guh-control/jsonrpc/jsontypes.cpp b/guh-control/jsonrpc/jsontypes.cpp index d4be34c0..d8e6763b 100644 --- a/guh-control/jsonrpc/jsontypes.cpp +++ b/guh-control/jsonrpc/jsontypes.cpp @@ -109,7 +109,7 @@ DeviceClass *JsonTypes::unpackDeviceClass(const QVariantMap &deviceClassMap, QOb Param *JsonTypes::unpackParam(const QVariantMap ¶mMap, QObject *parent) { - return new Param(paramMap.value("name").toString(), paramMap.value("value"), parent); + return new Param(paramMap.value("paramTypeId").toString(), paramMap.value("value"), parent); } ParamType *JsonTypes::unpackParamType(const QVariantMap ¶mTypeMap, QObject *parent) @@ -117,6 +117,7 @@ ParamType *JsonTypes::unpackParamType(const QVariantMap ¶mTypeMap, QObject * ParamType *paramType = new ParamType(parent); paramType->setId(paramTypeMap.value("id").toString()); paramType->setName(paramTypeMap.value("name").toString()); + paramType->setDisplayName(paramTypeMap.value("displayName").toString()); paramType->setType(paramTypeMap.value("type").toString()); paramType->setIndex(paramTypeMap.value("index").toInt()); paramType->setDefaultValue(paramTypeMap.value("defaultValue")); diff --git a/guh-control/main.cpp b/guh-control/main.cpp index d45df001..5cd505dc 100644 --- a/guh-control/main.cpp +++ b/guh-control/main.cpp @@ -18,7 +18,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include +#include #include #include #include @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QGuiApplication application(argc, argv); + QApplication application(argc, argv); application.setApplicationName("guh-control"); application.setOrganizationName("guh"); diff --git a/guh-control/models/valuelogsproxymodel.cpp b/guh-control/models/valuelogsproxymodel.cpp index bd7d0463..bc6656d1 100644 --- a/guh-control/models/valuelogsproxymodel.cpp +++ b/guh-control/models/valuelogsproxymodel.cpp @@ -4,7 +4,8 @@ ValueLogsProxyModel::ValueLogsProxyModel(QObject *parent) : LogsModel(parent) { - + m_minimumValue = QVariant(0); + m_maximumValue = QVariant(0); } void ValueLogsProxyModel::update() @@ -40,8 +41,6 @@ QVariant ValueLogsProxyModel::maximumValue() const void ValueLogsProxyModel::logsReply(const QVariantMap &data) { - m_busy = false; - emit busyChanged(); qDebug() << "logs reply"; @@ -84,7 +83,9 @@ void ValueLogsProxyModel::logsReply(const QVariantMap &data) continue; } QList tmp = entries[slot]; - tmp.append(entryMap.value("value")); + QVariant value = entryMap.value("value"); + value.convert(QVariant::Double); + tmp.append(value); entries[slot] = tmp; // qDebug() << "adding value to slot" << slot << entryMap.value("value") << QDateTime::fromMSecsSinceEpoch(entryMap.value("timestamp").toLongLong()); } @@ -114,15 +115,16 @@ void ValueLogsProxyModel::logsReply(const QVariantMap &data) continue; } LogEntry *entry = new LogEntry(startTime().addSecs(stepSize * i).addSecs(stepSize * .5), avg, this); -// qDebug() << "filling slot" << i << "average:" << avg << entry->timestamp(); m_list.append(entry); +// qDebug() << "**" << m_minimumValue << entry->value(); if (m_minimumValue.isNull() || entry->value() < m_minimumValue) { m_minimumValue = qRound(entry->value().toDouble()); } if (m_maximumValue.isNull() || entry->value() > m_maximumValue) { m_maximumValue = qRound(entry->value().toDouble()); } + qDebug() << "filling slot" << i << "average:" << avg << entry->timestamp().toString() << "min:" << m_minimumValue << "max:" << m_maximumValue; } @@ -133,4 +135,7 @@ void ValueLogsProxyModel::logsReply(const QVariantMap &data) emit countChanged(); qDebug() << "min" << minimumValue() << "max" << maximumValue(); + m_busy = false; + emit busyChanged(); + } diff --git a/guh-control/resources.qrc b/guh-control/resources.qrc index a6ea305e..cc2c0a09 100644 --- a/guh-control/resources.qrc +++ b/guh-control/resources.qrc @@ -101,5 +101,11 @@ ui/images/battery/battery-090.svg ui/images/battery/battery-100.svg ui/images/dialog-warning-symbolic.svg + ui/images/magic.svg + ui/EditDevicesPage.qml + ui/devicepages/ConfigureThingPage.qml + ui/magic/DeviceRulesPage.qml + ui/magic/SelectEventPage.qml + ui/magic/NewThingMagicPage.qml diff --git a/guh-control/ui/EditDevicesPage.qml b/guh-control/ui/EditDevicesPage.qml new file mode 100644 index 00000000..6c53f220 --- /dev/null +++ b/guh-control/ui/EditDevicesPage.qml @@ -0,0 +1,38 @@ +import QtQuick 2.4 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.2 +import "components" +import Guh 1.0 + +Page { + id: root + header: GuhHeader { + text: "Configure Things" + onBackPressed: pageStack.pop() + } + + ListView { + anchors.fill: parent + model: Engine.deviceManager.devices + delegate: ItemDelegate { + width: parent.width + contentItem: RowLayout { + spacing: app.margins + ColorIcon { + height: app.iconSize + width: height + name: app.interfaceToIcon(model.interfaces[0]) + color: app.guhAccent + } + + Label { + Layout.fillWidth: true + text: model.name + } + } + onClicked: { + pageStack.push(Qt.resolvedUrl("devicepages/ConfigureThingPage.qml"), {device: Engine.deviceManager.devices.get(index)}) + } + } + } +} diff --git a/guh-control/ui/MagicPage.qml b/guh-control/ui/MagicPage.qml index 7144ddb8..f6791eb4 100644 --- a/guh-control/ui/MagicPage.qml +++ b/guh-control/ui/MagicPage.qml @@ -7,10 +7,10 @@ Page { id: root header: GuhHeader { text: "Magic" - backButtonVisible: false + onBackPressed: pageStack.pop() HeaderButton { - imageSource: "images/add.svg" + imageSource: Qt.resolvedUrl("images/add.svg") onClicked: pageStack.push(Qt.resolvedUrl("magic/NewRulePage.qml")) } } diff --git a/guh-control/ui/MainPage.qml b/guh-control/ui/MainPage.qml index 51555955..f0d1d6a3 100644 --- a/guh-control/ui/MainPage.qml +++ b/guh-control/ui/MainPage.qml @@ -21,17 +21,26 @@ Page { IconMenuItem { iconSource: "../images/share.svg" text: "Configure things" + onTriggered: pageStack.push(Qt.resolvedUrl("EditDevicesPage.qml")) } IconMenuItem { iconSource: "../images/add.svg" - text: "Add a new thing..." + text: "Add a new thing" onTriggered: pageStack.push(Qt.resolvedUrl("NewDeviceWizard.qml")) } + MenuSeparator {} + IconMenuItem { + iconSource: "../images/magic.svg" + text: "Magic" + onTriggered: pageStack.push(Qt.resolvedUrl("MagicPage.qml")) + } + MenuSeparator {} IconMenuItem { iconSource: "../images/settings.svg" text: "App settings" onTriggered: pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) } + MenuSeparator {} IconMenuItem { iconSource: "../images/info.svg" text: "System information" diff --git a/guh-control/ui/NewDeviceWizard.qml b/guh-control/ui/NewDeviceWizard.qml index 6581e92b..5ba828e8 100644 --- a/guh-control/ui/NewDeviceWizard.qml +++ b/guh-control/ui/NewDeviceWizard.qml @@ -16,7 +16,7 @@ Page { } HeaderButton { - imageSource: "images/close.svg" + imageSource: "../images/close.svg" onClicked: pageStack.pop(); } } diff --git a/guh-control/ui/components/GuhHeader.qml b/guh-control/ui/components/GuhHeader.qml index 6016a55d..1d325271 100644 --- a/guh-control/ui/components/GuhHeader.qml +++ b/guh-control/ui/components/GuhHeader.qml @@ -42,6 +42,7 @@ ToolBar { Layout.fillHeight: true verticalAlignment: Text.AlignVCenter font.pixelSize: app.largeFont + elide: Text.ElideRight color: "#333" text: root.text.toUpperCase() } diff --git a/guh-control/ui/components/HeaderButton.qml b/guh-control/ui/components/HeaderButton.qml index 27e59a21..464d1d82 100644 --- a/guh-control/ui/components/HeaderButton.qml +++ b/guh-control/ui/components/HeaderButton.qml @@ -2,17 +2,16 @@ import QtQuick 2.5 import QtQuick.Controls 2.1 ToolButton { - property alias imageSource: image.source + property alias imageSource: image.name + property alias color: image.color contentItem: Item { height: 20 width: 20 - Image { + ColorIcon { id: image anchors.fill: parent anchors.margins: app.margins / 2 - sourceSize.height: height - sourceSize.width: width } } } diff --git a/guh-control/ui/customviews/SensorView.qml b/guh-control/ui/customviews/SensorView.qml index 4edf0aaf..8a3baa1f 100644 --- a/guh-control/ui/customviews/SensorView.qml +++ b/guh-control/ui/customviews/SensorView.qml @@ -1,6 +1,8 @@ import QtQuick 2.5 import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.1 import QtQuick.Layouts 1.3 +//import QtCharts 2.1 import "../components" import Guh 1.0 @@ -22,11 +24,25 @@ CustomViewBase { Component.onCompleted: updateTimer.start(); onAverageChanged: updateTimer.start() onStartTimeChanged: updateTimer.start(); + + onBusyChanged: { + if (!busy) { + +// lineSeries1.clear() +// splineSeries.clear() +// print("---", axisX.min, axisX.max) +// for (var i = 0; i < logsModel.count; i++) { +// print("adding", logsModel.get(i).timestamp, logsModel.get(i).value) +// lineSeries1.append(logsModel.get(i).timestamp, logsModel.get(i).value) +// splineSeries.append(logsModel.get(i).timestamp, logsModel.get(i).value) +// } + } + } } Timer { id: updateTimer - interval: 0 + interval: 10 repeat: false onTriggered: { print("updating:", logsModel.startTime) @@ -91,5 +107,70 @@ CustomViewBase { model: logsModel color: app.interfaceToColor(root.interfaceName) } + +// ChartView { +// id: chartView +// Layout.fillWidth: true +// Layout.preferredHeight: 300 +// animationOptions: ChartView.SeriesAnimations +// theme: ChartView.ChartThemeQt +// backgroundColor: Material.background +// property bool openGL: false +// property bool openGLSupported: true +// onOpenGLChanged: { +// if (openGLSupported) { +// series("signal 1").useOpenGL = openGL; +// series("signal 2").useOpenGL = openGL; +// } +// } +// Component.onCompleted: { +// if (!series("signal 1").useOpenGL) { +// openGLSupported = false +// openGL = false +// } +// } + +// ValueAxis { +// id: axisY1 +// min: logsModel.minimumValue - 1 +// max: logsModel.maximumValue + 1 +// } + +// DateTimeAxis { +// id: axisX +// min: logsModel.startTime +// max: logsModel.endTime +// format: { +// switch (logsModel.average) { +// case ValueLogsProxyModel.AverageMinute: +// case ValueLogsProxyModel.AverageHourly: +// case ValueLogsProxyModel.AverageQuarterHour: +// return "hh:mm" +// } +// return "ddd
dd.MM.
hh:mm" +// } +// } + + +// AreaSeries { +// axisX: axisX +// axisY: axisY1 +// borderWidth: 0 +// name: app.interfaceToString(interfaceName) +// borderColor: app.interfaceToColor(interfaceName) +// color: Qt.rgba(borderColor.r, borderColor.g, borderColor.b, .4) +// useOpenGL: chartView.openGL +// upperSeries: LineSeries { +// id: lineSeries1 +// } +// } +// SplineSeries { +// id: splineSeries +// axisX: axisX +// axisY: axisY1 +// width: 3 +// color: app.interfaceToColor(interfaceName) +// } +// } } } diff --git a/guh-control/ui/devicelistpages/LightsDeviceListPage.qml b/guh-control/ui/devicelistpages/LightsDeviceListPage.qml index 0a00aa0c..0583d901 100644 --- a/guh-control/ui/devicelistpages/LightsDeviceListPage.qml +++ b/guh-control/ui/devicelistpages/LightsDeviceListPage.qml @@ -100,9 +100,7 @@ Page { } onClicked: { - if (deviceClass.interfaces.indexOf("colorlight") >= 0) { - pageStack.push(Qt.resolvedUrl("../devicepages/ColorLightDevicePage.qml"), {device: devicesProxy.get(index)}) - } + pageStack.push(Qt.resolvedUrl("../devicepages/ColorLightDevicePage.qml"), {device: devicesProxy.get(index)}) } } } diff --git a/guh-control/ui/devicepages/ColorLightDevicePage.qml b/guh-control/ui/devicepages/ColorLightDevicePage.qml index 264fa321..628b0005 100644 --- a/guh-control/ui/devicepages/ColorLightDevicePage.qml +++ b/guh-control/ui/devicepages/ColorLightDevicePage.qml @@ -5,21 +5,8 @@ import Guh 1.0 import "../components" import "../actiondelegates" -Page { +DevicePageBase { id: root - property var device: null - readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) - - - header: GuhHeader { - text: device.name - onBackPressed: pageStack.pop() - - HeaderButton { - imageSource: "../images/info.svg" - onClicked: pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device}) - } - } ColumnLayout { anchors.fill: parent @@ -96,8 +83,9 @@ Page { Layout.fillWidth: true Layout.margins: app.margins property var actionType: root.deviceClass.actionTypes.findByName("colorTemperature") - property var ctState: root.device.states.getState(actionType.id) - ct: ctState.value + property var ctState: actionType ? root.device.states.getState(actionType.id) : null + ct: ctState ? ctState.value : 0 + visible: root.deviceClass.interfaces.indexOf("colorlight") >= 0 height: 80 @@ -132,13 +120,19 @@ Page { Layout.fillWidth: true Layout.fillHeight: true actionType: root.deviceClass.actionTypes.findByName("color") - actionState: root.device.states.getState(actionType.id).value + actionState: actionType ? root.device.states.getState(actionType.id).value : null + visible: root.deviceClass.interfaces.indexOf("colorlight") >= 0 onExecuteAction: { Engine.deviceManager.executeAction(root.device.id, actionType.id, params) } } + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } } diff --git a/guh-control/ui/devicepages/ConfigureThingPage.qml b/guh-control/ui/devicepages/ConfigureThingPage.qml new file mode 100644 index 00000000..789ca806 --- /dev/null +++ b/guh-control/ui/devicepages/ConfigureThingPage.qml @@ -0,0 +1,53 @@ +import QtQuick 2.8 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.2 +import Guh 1.0 +import "../components" + +Page { + id: root + property var device: null + readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) + + header: GuhHeader { + text: root.device.name + onBackPressed: pageStack.pop() + + HeaderButton { + imageSource: "../images/delete.svg" + color: "red" + onClicked: { + Engine.deviceManager.removeDevice(root.device.id) + } + } + } + + Connections { + target: Engine.deviceManager + onRemoveDeviceReply: { + if (params.deviceError === "DeviceErrorNoError") { + pageStack.pop(); + return; + } + print("Remove device error!", params) + } + } + + ListView { + anchors.fill: parent + model: root.device.params + delegate: ItemDelegate { + width: parent.width + contentItem: RowLayout { + Label { + text: root.deviceClass.paramTypes.getParamType(model.id).displayName + } + Label { + Layout.fillWidth: true + text: model.value + horizontalAlignment: Text.AlignRight + } + } + } + } +} diff --git a/guh-control/ui/devicepages/DevicePageBase.qml b/guh-control/ui/devicepages/DevicePageBase.qml index c1e4083b..d431112d 100644 --- a/guh-control/ui/devicepages/DevicePageBase.qml +++ b/guh-control/ui/devicepages/DevicePageBase.qml @@ -11,6 +11,21 @@ Page { default property alias data: contentItem.data + header: GuhHeader { + text: device.name + onBackPressed: pageStack.pop() + + HeaderButton { + imageSource: "../images/magic.svg" + onClicked: pageStack.push(Qt.resolvedUrl("../magic/DeviceRulesPage.qml"), {device: root.device}) + } + + HeaderButton { + imageSource: "../images/info.svg" + onClicked: pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device}) + } + } + Pane { id: infoPane visible: batteryState !== null || (connectedState !== null && connectedState.value === false) diff --git a/guh-control/ui/devicepages/GenericDevicePage.qml b/guh-control/ui/devicepages/GenericDevicePage.qml index 06729678..c5e6c85a 100644 --- a/guh-control/ui/devicepages/GenericDevicePage.qml +++ b/guh-control/ui/devicepages/GenericDevicePage.qml @@ -7,16 +7,6 @@ import "../components" DevicePageBase { id: root - header: GuhHeader { - text: device.name - onBackPressed: pageStack.pop() - - HeaderButton { - imageSource: "../images/info.svg" - onClicked: pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device}) - } - } - Flickable { id: flickable anchors.fill: parent diff --git a/guh-control/ui/devicepages/WeatherDevicePage.qml b/guh-control/ui/devicepages/WeatherDevicePage.qml index 549b7f2a..f314bac4 100644 --- a/guh-control/ui/devicepages/WeatherDevicePage.qml +++ b/guh-control/ui/devicepages/WeatherDevicePage.qml @@ -5,26 +5,33 @@ import Guh 1.0 import "../components" import "../customviews" -Page { +DevicePageBase { id: root - property var device: null - readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) - - header: GuhHeader { - text: device.name - onBackPressed: pageStack.pop() - - HeaderButton { - imageSource: "../images/info.svg" - onClicked: pageStack.push(Qt.resolvedUrl("GenericDeviceStateDetailsPage.qml"), {device: root.device}) + Flickable { + anchors.fill: parent + clip: true + contentHeight: content.implicitHeight + ColumnLayout { + id: content + width: parent.width + WeatherView { + Layout.fillWidth: true + device: root.device + deviceClass: root.deviceClass + } + SensorView { + Layout.fillWidth: true + device: root.device + deviceClass: root.deviceClass + interfaceName: "temperaturesensor" + } + SensorView { + Layout.fillWidth: true + device: root.device + deviceClass: root.deviceClass + interfaceName: "humiditysensor" + } } } - - WeatherView { - anchors.fill: parent - device: root.device - deviceClass: root.deviceClass - } - } diff --git a/guh-control/ui/images/magic.svg b/guh-control/ui/images/magic.svg new file mode 100644 index 00000000..94043003 --- /dev/null +++ b/guh-control/ui/images/magic.svg @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/guh-control/ui/magic/DeviceRulesPage.qml b/guh-control/ui/magic/DeviceRulesPage.qml new file mode 100644 index 00000000..1cfac54b --- /dev/null +++ b/guh-control/ui/magic/DeviceRulesPage.qml @@ -0,0 +1,75 @@ +import QtQuick 2.8 +import QtQuick.Controls 2.1 +import "../components" +import Guh 1.0 + +Page { + id: root + + property var device: null + + header: GuhHeader { + text: qsTr("Magic involving %1").arg(root.device.name) + onBackPressed: pageStack.pop() + + HeaderButton { + imageSource: "../images/add.svg" + visible: rulesListView.count > 0 + onClicked: addRule() + } + } + + function addRule() { + pageStack.push(Qt.resolvedUrl("NewThingMagicPage.qml"), {device: root.device, text: "Add magic"}) + } + + ListView { + id: rulesListView + anchors.fill: parent + + model: RulesFilterModel { + id: rulesFilterModel + rules: Engine.ruleManager.rules + filterEventDeviceId: root.device.id + } + + delegate: ItemDelegate { + text: model.name + } + } + + Column { + anchors.centerIn: parent + width: parent.width - app.margins * 2 + spacing: app.margins * 2 + visible: rulesListView.count == 0 + + Label { + width: parent.width + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + text: qsTr("There's no magic involving %1.").arg(root.device.name) + font.pixelSize: app.largeFont + } + Label { + width: parent.width + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + text: qsTr("Add some using the wizard stick!") + font.pixelSize: app.largeFont + } + + AbstractButton { + height: app.iconSize * 4 + width: height + anchors.horizontalCenter: parent.horizontalCenter + + ColorIcon { + anchors.fill: parent + name: "../images/magic.svg" + } + + onClicked: addRule() + } + } +} diff --git a/guh-control/ui/magic/NewRulePage.qml b/guh-control/ui/magic/NewRulePage.qml index da473cce..9f139d54 100644 --- a/guh-control/ui/magic/NewRulePage.qml +++ b/guh-control/ui/magic/NewRulePage.qml @@ -4,6 +4,9 @@ import "../components" Page { id: root + + property var device: null + header: GuhHeader { text: "New rule" onBackPressed: pageStack.pop() diff --git a/guh-control/ui/magic/NewThingMagicPage.qml b/guh-control/ui/magic/NewThingMagicPage.qml new file mode 100644 index 00000000..3936a2e6 --- /dev/null +++ b/guh-control/ui/magic/NewThingMagicPage.qml @@ -0,0 +1,75 @@ +import QtQuick 2.4 +import QtQuick.Controls 2.1 +import "../components" +import Guh 1.0 + +Page { + id: root + property alias text: header.text + property var device: null + readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) + + header: GuhHeader { + id: header + onBackPressed: pageStack.pop() + } + + ListModel { + id: eventModel + ListElement { interfaceName: "temperaturesensor"; text: "When it's freezing..."; identifier: "freeze"} + ListElement { interfaceName: "battery"; text: "When the device runs out of battery..."; identifier: "lowBattery"} + ListElement { interfaceName: "weather"; text: "When it starts raining..."; identifier: "rain" } + } + + ListModel { + id: actionModel + ListElement { interfaceName: "light"; text: "Switch light when..."; identifier: "switchLight"} + ListElement { interfaceName: "dimmablelight"; text: "Dim light when..."; identifier: "dimLight"} + ListElement { interfaceName: "colorlight"; text: "Set light color when..."; identifier: "colorLight" } + ListElement { interfaceName: "mediacontroller"; text: "Pause playback when..."; identifier: "pausePlayback" } + ListElement { interfaceName: "mediacontroller"; text: "Resume playback when..."; identifier: "resumePlayback" } + ListElement { interfaceName: "extendedvolumecontroller"; text: "Set volume..."; identifier: "setVolume" } + ListElement { interfaceName: "extendedvolumecontroller"; text: "Mute when..."; identifier: "mute" } + ListElement { interfaceName: "extendedvolumecontroller"; text: "Unmute when..."; identifier: "unmute" } + ListElement { interfaceName: "notifications"; text: "Notify me when..."; identifier: "notify" } + } + + function entrySelected(identifier) { + switch (identifier) { + case "freeze": + pageStack.push(Qt.resolvedUrl("SelectActionPage.qml"), {device: root.device }) + } + } + + onDeviceClassChanged: { + actualModel.clear() + print("device supports interfaces", deviceClass.interfaces) + for (var i = 0; i < eventModel.count; i++) { + print("event is for interface", eventModel.get(i).interfaceName) + if (deviceClass.interfaces.indexOf(eventModel.get(i).interfaceName) >= 0) { + actualModel.append(eventModel.get(i)) + } + } + print("huh") + for (var i = 0; i < actionModel.count; i++) { + print("action is for interface", actionModel.get(i).interfaceName) + if (deviceClass.interfaces.indexOf(actionModel.get(i).interfaceName) >= 0) { + actualModel.append(actionModel.get(i)) + } + } + } + + ListView { + anchors.fill: parent + model: ListModel { + id: actualModel + } + + delegate: ItemDelegate { + width: parent.width + text: model.text + + onClicked: root.entrySelected(model.identifier) + } + } +} diff --git a/guh-control/ui/magic/SelectActionPage.qml b/guh-control/ui/magic/SelectActionPage.qml index 865c0c62..a4275e2d 100644 --- a/guh-control/ui/magic/SelectActionPage.qml +++ b/guh-control/ui/magic/SelectActionPage.qml @@ -21,6 +21,45 @@ Page { onBackPressed: pageStack.pop() } + ListModel { + id: actionModel + ListElement { interfaceName: "light"; text: "Switch lights..."; identifier: "switchLights" } + ListElement { interfaceName: "mediacontroller"; text: "Control media playback..."; identifier: "controlMedia" } + ListElement { interfaceName: "extendedvolumecontroller"; text: "Mute media playback..."; identifier: "muteMedia" } + ListElement { interfaceName: "notifications"; text: "Notify me..."; identifier: "notify" } + ListElement { interfaceName: ""; text: "Manually configure an action..."; identifier: "manualAction" } + } + + DevicesProxy { + id: ifaceFilterModel + devices: Engine.deviceManager.devices + } + + Component.onCompleted: { + actualModel.clear() + for (var i = 0; i < actionModel.count; i++) { + ifaceFilterModel.filterInterface = actionModel.get(i).interfaceName; + if (actionModel.get(i).interfaceName === "" || ifaceFilterModel.count > 0) { + actualModel.append(actionModel.get(i)) + } + } + } + + function actionSelected(identifier) { + switch (identifier) { + case "switchLights": + pageStack.push(switchLightsCompoent) + break; + case "controlMedia": + break; + case "muteMedia": + break; + case "manualAction": + pageStack.push(selectDeviceComponent) + break; + } + } + ColumnLayout { anchors.fill: parent @@ -35,26 +74,13 @@ Page { Layout.fillWidth: true Layout.fillHeight: true model: ListModel { - ListElement { name: "switchLights"; text: "Switch lights..." } - ListElement { name: "controlMedia"; text: "Control media playback..." } - ListElement { name: "manualAction"; text: "Manually configure an action..." } + id: actualModel } delegate: ItemDelegate { width: parent.width text: model.text onClicked: { - switch (model.name) { - case "switchLights": - pageStack.push(switchLightsCompoent) - break; - case "controlMedia": - break; - case "muteMedia": - break; - case "manualAction": - pageStack.push(selectDeviceComponent) - break; - } + root.actionSelected(model.identifier) } } } diff --git a/guh-control/ui/magic/SelectEventPage.qml b/guh-control/ui/magic/SelectEventPage.qml new file mode 100644 index 00000000..bb49b866 --- /dev/null +++ b/guh-control/ui/magic/SelectEventPage.qml @@ -0,0 +1,46 @@ +import QtQuick 2.4 +import QtQuick.Controls 2.1 +import "../components" +import Guh 1.0 + +Page { + id: root + property alias text: header.text + property var device: null + readonly property var deviceClass: Engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId) + + header: GuhHeader { + id: header + onBackPressed: pageStack.pop() + } + + ListModel { + id: eventModel + ListElement { interfaceName: "temperaturesensor"; text: "When it's freezing..."; event: "freeze"} + ListElement { interfaceName: "battery"; text: "When the device runs out of battery..."; event: "lowBattery"} + ListElement { interfaceName: "weather"; text: "When it starts raining..."; event: "rain" } + } + + onDeviceClassChanged: { + actualModel.clear() + print("device supports interfaces", deviceClass.interfaces) + for (var i = 0; i < eventModel.count; i++) { + print("event is for interface", eventModel.get(i).interfaceName) + if (deviceClass.interfaces.indexOf(eventModel.get(i).interfaceName) >= 0) { + actualModel.append(eventModel.get(i)) + } + } + } + + ListView { + anchors.fill: parent + model: ListModel { + id: actualModel + } + + delegate: ItemDelegate { + text: model.text + } + + } +} diff --git a/guh-control/ui/main.qml b/guh-control/ui/main.qml index ed872f4b..c682fdf5 100644 --- a/guh-control/ui/main.qml +++ b/guh-control/ui/main.qml @@ -117,18 +117,28 @@ ApplicationWindow { return "Gateways" case "notifications": return "Notifications" + case "temperaturesensor": + return "Temperature"; + case "humiditysensor": + return "Humidity"; } } function interfaceToIcon(name) { switch (name) { case "light": + case "colorlight": + case "dimmablelight": return Qt.resolvedUrl("images/torch-on.svg") case "sensor": return Qt.resolvedUrl("images/sensors.svg") case "media": + case "mediacontroller": return Qt.resolvedUrl("images/mediaplayer-app-symbolic.svg") case "button": + case "longpressbutton": + case "multibutton": + case "longpressmultibutton": return Qt.resolvedUrl("images/system-shutdown.svg") case "weather": return Qt.resolvedUrl("images/weather-app-symbolic.svg") diff --git a/libguh-common/types/logentry.h b/libguh-common/types/logentry.h index 4d2f807c..3ae85a4a 100644 --- a/libguh-common/types/logentry.h +++ b/libguh-common/types/logentry.h @@ -10,6 +10,7 @@ class LogEntry : public QObject Q_OBJECT Q_PROPERTY(QVariant value READ value CONSTANT) + Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT) Q_PROPERTY(QString timeString READ timeString CONSTANT) Q_PROPERTY(QString dayString READ dayString CONSTANT) Q_PROPERTY(QString dateString READ dateString CONSTANT) diff --git a/libguh-common/types/param.cpp b/libguh-common/types/param.cpp index 4446c077..43cc8afb 100644 --- a/libguh-common/types/param.cpp +++ b/libguh-common/types/param.cpp @@ -22,22 +22,22 @@ #include "param.h" -Param::Param(const QString &name, const QVariant &value, QObject *parent) : +Param::Param(const QString &id, const QVariant &value, QObject *parent) : QObject(parent), - m_name(name), + m_id(id), m_value(value) { } -QString Param::name() const +QString Param::id() const { - return m_name; + return m_id; } -void Param::setName(const QString &name) +void Param::setId(const QString &id) { - m_name = name; - emit nameChanged(); + m_id = id; + emit idChanged(); } QVariant Param::value() const diff --git a/libguh-common/types/param.h b/libguh-common/types/param.h index 41c44096..95604f47 100644 --- a/libguh-common/types/param.h +++ b/libguh-common/types/param.h @@ -30,24 +30,24 @@ class Param : public QObject { Q_OBJECT - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged) Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) public: - Param(const QString &name = QString(), const QVariant &value = QVariant(), QObject *parent = 0); + Param(const QString &id = QString(), const QVariant &value = QVariant(), QObject *parent = 0); - QString name() const; - void setName(const QString &name); + QString id() const; + void setId(const QString &id); QVariant value() const; void setValue(const QVariant &value); private: - QString m_name; + QString m_id; QVariant m_value; signals: - void nameChanged(); + void idChanged(); void valueChanged(); }; diff --git a/libguh-common/types/params.cpp b/libguh-common/types/params.cpp index 392a3323..da2525af 100644 --- a/libguh-common/types/params.cpp +++ b/libguh-common/types/params.cpp @@ -44,10 +44,10 @@ Param *Params::get(int index) const return m_params.at(index); } -Param *Params::getParam(QString name) const +Param *Params::getParam(QString paramTypeId) const { foreach (Param *param, m_params) { - if (param->name() == name) { + if (param->id() == paramTypeId) { return param; } } @@ -71,9 +71,9 @@ QVariant Params::data(const QModelIndex &index, int role) const return QVariant(); Param *param = m_params.at(index.row()); - if (role == NameRole) { - return param->name(); - } else if (role == ValueRole) { + if (role == RoleId) { + return param->id(); + } else if (role == RoleValue) { return param->value(); } return QVariant(); @@ -97,7 +97,7 @@ void Params::clearModel() QHash Params::roleNames() const { QHash roles; - roles[NameRole] = "name"; - roles[ValueRole] = "value"; + roles[RoleId] = "id"; + roles[RoleValue] = "value"; return roles; } diff --git a/libguh-common/types/params.h b/libguh-common/types/params.h index 278b15d4..40cfb41d 100644 --- a/libguh-common/types/params.h +++ b/libguh-common/types/params.h @@ -31,9 +31,9 @@ class Params : public QAbstractListModel { Q_OBJECT public: - enum ParamRole { - NameRole = Qt::DisplayRole, - ValueRole + enum RoleId { + RoleId, + RoleValue }; explicit Params(QObject *parent = 0); @@ -42,7 +42,7 @@ public: Q_INVOKABLE int count() const; Q_INVOKABLE Param *get(int index) const; - Q_INVOKABLE Param *getParam(QString name) const; + Q_INVOKABLE Param *getParam(QString paramTypeId) const; Q_INVOKABLE int paramCount() const; diff --git a/libguh-common/types/paramtype.cpp b/libguh-common/types/paramtype.cpp index adbda1d0..a567e6ac 100644 --- a/libguh-common/types/paramtype.cpp +++ b/libguh-common/types/paramtype.cpp @@ -57,6 +57,16 @@ void ParamType::setName(const QString &name) m_name = name; } +QString ParamType::displayName() const +{ + return m_displayName; +} + +void ParamType::setDisplayName(const QString &displayName) +{ + m_displayName = displayName; +} + QString ParamType::type() const { return m_type; diff --git a/libguh-common/types/paramtype.h b/libguh-common/types/paramtype.h index 3b3e44da..3c40476f 100644 --- a/libguh-common/types/paramtype.h +++ b/libguh-common/types/paramtype.h @@ -35,6 +35,7 @@ class ParamType : public QObject Q_OBJECT Q_PROPERTY(QUuid 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) Q_PROPERTY(int index READ index CONSTANT) Q_PROPERTY(QVariant defaultValue READ defaultValue CONSTANT) @@ -55,6 +56,9 @@ public: QString name() const; void setName(const QString &name); + QString displayName() const; + void setDisplayName(const QString &displayName); + QString type() const; void setType(const QString &type); @@ -88,6 +92,7 @@ public: private: QUuid m_id; QString m_name; + QString m_displayName; QString m_type; int m_index; QVariant m_defaultValue; diff --git a/libguh-common/types/paramtypes.cpp b/libguh-common/types/paramtypes.cpp index d9d772f1..5f0f28c0 100644 --- a/libguh-common/types/paramtypes.cpp +++ b/libguh-common/types/paramtypes.cpp @@ -37,10 +37,10 @@ ParamType *ParamTypes::get(int index) const return m_paramTypes.at(index); } -ParamType *ParamTypes::getParamType(const QString &name) const +ParamType *ParamTypes::getParamType(const QString &id) const { foreach (ParamType *paramType, m_paramTypes) { - if (paramType->name() == name) { + if (paramType->id() == id) { return paramType; } } diff --git a/libguh-common/types/paramtypes.h b/libguh-common/types/paramtypes.h index 9532a87d..721b5c5a 100644 --- a/libguh-common/types/paramtypes.h +++ b/libguh-common/types/paramtypes.h @@ -49,7 +49,7 @@ public: QList paramTypes(); Q_INVOKABLE ParamType *get(int index) const; - Q_INVOKABLE ParamType *getParamType(const QString &name) const; + Q_INVOKABLE ParamType *getParamType(const QString &id) const; int rowCount(const QModelIndex & parent = QModelIndex()) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;