diff --git a/libnymea-app-core/models/logsmodelng.cpp b/libnymea-app-core/models/logsmodelng.cpp index 11b821eb..71b8bdc1 100644 --- a/libnymea-app-core/models/logsmodelng.cpp +++ b/libnymea-app-core/models/logsmodelng.cpp @@ -163,13 +163,13 @@ void LogsModelNg::setViewStartTime(const QDateTime &viewStartTime) QVariant LogsModelNg::minValue() const { - qDebug() << "returning min value" << m_minValue; +// qDebug() << "returning min value" << m_minValue; return m_minValue; } QVariant LogsModelNg::maxValue() const { - qDebug() << "returning max value" << m_maxValue; +// qDebug() << "returning max value" << m_maxValue; return m_maxValue; } @@ -177,8 +177,6 @@ void LogsModelNg::logsReply(const QVariantMap &data) { // qDebug() << "logs reply" << data; - m_busy = false; - emit busyChanged(); int offset = data.value("params").toMap().value("offset").toInt(); int count = data.value("params").toMap().value("count").toInt(); @@ -221,20 +219,20 @@ void LogsModelNg::logsReply(const QVariantMap &data) if (i > 0) { LogEntry *newerEntry = newBlock.at(i - 1); if (newerEntry->value().toBool() != entry->value().toBool()) { - qDebug() << "Adding bool line series point:" << (newerEntry->timestamp().addSecs(-1)) << newerEntry->timestamp().addSecs(-1).toMSecsSinceEpoch() << (entry->value().toBool() ? 1 : 0) << "(correction)"; +// qDebug() << "Adding bool line series point:" << (newerEntry->timestamp().addSecs(-1)) << newerEntry->timestamp().addSecs(-1).toMSecsSinceEpoch() << (entry->value().toBool() ? 1 : 0) << "(correction)"; m_graphSeries->append(QPointF(newerEntry->timestamp().addSecs(-1).toMSecsSinceEpoch(), entry->value().toBool() ? 1 : 0)); } } if (m_graphSeries->count() == 0) { // If it's the first one, make sure we add an ending point at 1 - qDebug() << "Adding bool line series point:" << QDateTime::currentDateTime() << QDateTime::currentDateTime().toMSecsSinceEpoch() - 1 << (entry->value().toBool() ? 1 : 0) << "(beginning)"; +// qDebug() << "Adding bool line series point:" << QDateTime::currentDateTime() << QDateTime::currentDateTime().toMSecsSinceEpoch() - 1 << (entry->value().toBool() ? 1 : 0) << "(beginning)"; m_graphSeries->append(QPointF(QDateTime::currentDateTime().toMSecsSinceEpoch(), 1)); m_graphSeries->append(QPointF(QDateTime::currentDateTime().toMSecsSinceEpoch(), entry->value().toBool() ? 1 : 0)); } else if (i == 0) { // Adding a new batch... remove the last appended 1 from the previous batch m_graphSeries->remove(m_graphSeries->count() - 1); } - qDebug() << "Adding bool line series point:" << entry->timestamp() << entry->timestamp().toMSecsSinceEpoch() << (entry->value().toBool() ? 1 : 0); +// qDebug() << "Adding bool line series point:" << entry->timestamp() << entry->timestamp().toMSecsSinceEpoch() << (entry->value().toBool() ? 1 : 0); m_graphSeries->append(QPointF(entry->timestamp().toMSecsSinceEpoch(), entry->value().toBool() ? 1 : 0)); if (i == newBlock.count() - 1) { // End the batch at 1 again @@ -248,8 +246,10 @@ void LogsModelNg::logsReply(const QVariantMap &data) // m_graphSeries->append(QPointF(newerEntry->timestamp().toMSecsSinceEpoch() - 1, entry->value().toReal())); // } // } + if (m_graphSeries->count() == 0) { - m_graphSeries->insert(0, QPointF(QDateTime::currentDateTime().toMSecsSinceEpoch(), entry->value().toReal())); + qDebug() << "Adding 1st line series point:" << (offset + i) << QDateTime::currentDateTime().toMSecsSinceEpoch() << entry->value().toReal(); + m_graphSeries->append(QPointF(QDateTime::currentDateTime().toMSecsSinceEpoch(), entry->value().toReal())); } qDebug() << "Adding line series point:" << (offset + i) << entry->timestamp().toMSecsSinceEpoch() << (entry->value().toReal()); m_graphSeries->append(QPointF(entry->timestamp().toMSecsSinceEpoch(), entry->value().toReal())); @@ -264,7 +264,7 @@ void LogsModelNg::logsReply(const QVariantMap &data) } endInsertRows(); emit countChanged(); - qDebug() << "min" << m_minValue << "max" << m_maxValue << "newMin" << newMin << "newMax" << newMax; +// qDebug() << "min" << m_minValue << "max" << m_maxValue << "newMin" << newMin << "newMax" << newMax; if (m_minValue != newMin) { m_minValue = newMin; emit minValueChanged(); @@ -274,6 +274,9 @@ void LogsModelNg::logsReply(const QVariantMap &data) emit maxValueChanged(); } + m_busy = false; + emit busyChanged(); + if (m_viewStartTime.isValid() && m_list.count() > 0 && m_list.last()->timestamp() > m_viewStartTime && canFetchMore()) { fetchMore(); } @@ -282,7 +285,7 @@ void LogsModelNg::logsReply(const QVariantMap &data) void LogsModelNg::fetchMore(const QModelIndex &parent) { Q_UNUSED(parent) - qDebug() << "fetchMore called"; +// qDebug() << "fetchMore called"; if (!m_engine) { qWarning() << "Cannot update. Engine not set"; @@ -326,13 +329,13 @@ void LogsModelNg::fetchMore(const QModelIndex &parent) params.insert("offset", m_list.count()); m_engine->jsonRpcClient()->sendCommand("Logging.GetLogEntries", params, this, "logsReply"); - qDebug() << "GetLogEntries called"; +// qDebug() << "GetLogEntries called"; } bool LogsModelNg::canFetchMore(const QModelIndex &parent) const { Q_UNUSED(parent) - qDebug() << "canFetchMore" << (m_engine && m_canFetchMore); +// qDebug() << "canFetchMore" << (m_engine && m_canFetchMore); return m_engine && m_canFetchMore; } @@ -364,6 +367,15 @@ void LogsModelNg::newLogEntryReceived(const QVariantMap &data) m_list.prepend(entry); if (m_graphSeries) { m_graphSeries->insert(0, QPointF(entry->timestamp().toMSecsSinceEpoch(), entry->value().toReal())); + if (m_minValue > entry->value().toReal()) { + m_minValue = entry->value().toReal(); + emit minValueChanged(); + } + if (m_maxValue < entry->value().toReal()) { + m_maxValue = entry->value().toReal(); + emit maxValueChanged(); + } + } endInsertRows(); emit countChanged(); diff --git a/nymea-app/images.qrc b/nymea-app/images.qrc index 337433fe..37ca322f 100644 --- a/nymea-app/images.qrc +++ b/nymea-app/images.qrc @@ -150,5 +150,6 @@ ui/images/weather-app-symbolic.svg ui/images/zoom-out.svg ui/images/zoom-in.svg + ui/images/smartmeter.svg diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index ee60f6c4..21499768 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -57,6 +57,7 @@ ui/customviews/MediaControllerView.qml ui/customviews/SensorView.qml ui/customviews/NotificationsView.qml + ui/customviews/ExtendedVolumeController.qml ui/devicepages/MediaDevicePage.qml ui/devicepages/ButtonDevicePage.qml ui/devicepages/GenericDeviceStateDetailsPage.qml @@ -133,6 +134,6 @@ ../LICENSE ui/customviews/GenericTypeGraphPre110.qml ui/customviews/GenericTypeGraph.qml - ui/customviews/SensorChart.qml + ui/devicepages/SmartMeterDevicePage.qml diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml index dd88c856..65116fc8 100644 --- a/nymea-app/ui/Nymea.qml +++ b/nymea-app/ui/Nymea.qml @@ -53,7 +53,7 @@ ApplicationWindow { rootItem.handleCloseEvent(close) } - property var supportedInterfaces: ["light", "weather", "sensor", "media", "garagegate", "awning", "shutter", "blind", "accesscontrol", "button", "notifications", "inputtrigger", "outputtrigger", "gateway"] + property var supportedInterfaces: ["light", "weather", "sensor", "media", "garagegate", "awning", "shutter", "blind", "smartmeter", "accesscontrol", "button", "notifications", "inputtrigger", "outputtrigger", "gateway"] function interfaceToString(name) { switch(name) { case "light": @@ -93,6 +93,8 @@ ApplicationWindow { return qsTr("Garage gates"); case "accesscontrol": return qsTr("Access control"); + case "smartmeter": + return qsTr("Smart meter"); case "uncategorized": return qsTr("Uncategorized") default: @@ -149,8 +151,6 @@ ApplicationWindow { return Qt.resolvedUrl("images/network-wired-symbolic.svg") case "notifications": return Qt.resolvedUrl("images/notification.svg") - case "connectable": - return Qt.resolvedUrl("images/stock_link.svg") case "inputtrigger": return Qt.resolvedUrl("images/attention.svg") case "outputtrigger": @@ -175,6 +175,14 @@ ApplicationWindow { return Qt.resolvedUrl("images/fingerprint.svg") case "accesscontrol": return Qt.resolvedUrl("images/network-secure.svg"); + case "smartmeter": + case "smartmeterconsumer": + case "smartmeterproducer": + case "extendedsmartmeterconsumer": + case "extendedsmartmeterproducer": + return Qt.resolvedUrl("images/smartmeter.svg") + case "connectable": + return Qt.resolvedUrl("images/stock_link.svg") default: console.warn("InterfaceToIcon: Unhandled interface", name) } @@ -187,7 +195,11 @@ ApplicationWindow { "moisturesensor":"blue", "lightsensor": "orange", "conductivitysensor": "green", - "pressuresensor": "grey" + "pressuresensor": "grey", + "smartmeterproducer": "lightgreen", + "smartmeterconsumer": "orange", + "extendedsmartmeterproducer": "blue", + "extendedsmartmeterconsumer": "blue" } function interfaceToColor(name) { @@ -244,6 +256,8 @@ ApplicationWindow { page = "NotificationsDevicePage.qml"; } else if (interfaceList.indexOf("fingerprintreader") >= 0) { page = "FingerprintReaderDevicePage.qml"; + } else if (interfaceList.indexOf("smartmeter") >= 0) { + page = "SmartMeterDevicePage.qml" } else { page = "GenericDevicePage.qml"; } diff --git a/nymea-app/ui/customviews/GenericTypeGraph.qml b/nymea-app/ui/customviews/GenericTypeGraph.qml index 4b8c3a21..964b8827 100644 --- a/nymea-app/ui/customviews/GenericTypeGraph.qml +++ b/nymea-app/ui/customviews/GenericTypeGraph.qml @@ -9,10 +9,11 @@ import QtCharts 2.2 Item { id: root + implicitHeight: width * .6 property var device: null property var stateType: null - property var valueState: device.states.getState(stateType.id) + readonly property var valueState: device.states.getState(stateType.id) readonly property var deviceClass: engine.deviceManager.deviceClasses.getDeviceClass(device.deviceClassId); readonly property bool hasConnectable: deviceClass.interfaces.indexOf("connectable") >= 0 readonly property var connectedStateType: hasConnectable ? deviceClass.stateTypes.findByName("connected") : null @@ -94,8 +95,8 @@ Item { ValueAxis { id: yAxis - min: logsModelNg.minValue - logsModelNg.minValue * .05 - max: logsModelNg.maxValue + logsModelNg.maxValue * .05 + max: logsModelNg.maxValue + Math.abs(logsModelNg.maxValue * .05) + min: logsModelNg.minValue - Math.abs(logsModelNg.minValue * .05) labelsFont.pixelSize: app.smallFont labelsColor: app.foregroundColor tickCount: chartView.height / 40 @@ -179,10 +180,14 @@ Item { min: { var date = new Date(); - date.setHours(date.getHours() - 6); + date.setTime(date.getTime() - (1000 * 60 * 60 * 6) + 2000); + return date; + } + max: { + var date = new Date(); + date.setTime(date.getTime() + 2000) return date; } - max: new Date() } AreaSeries { @@ -209,8 +214,38 @@ Item { name: root.stateType.displayName borderColor: root.color borderWidth: 4 + lowerSeries: LineSeries { + id: lineSeries0 + XYPoint { x: xAxis.max.getTime(); y: 0 } + XYPoint { x: xAxis.min.getTime(); y: 0 } + } + upperSeries: LineSeries { id: lineSeries1 + onPointAdded: { + var newPoint = lineSeries1.at(index) + + if (newPoint.x > lineSeries0.at(0).x) { + lineSeries0.replace(0, newPoint.x, 0) + } + if (newPoint.x < lineSeries0.at(1).x) { + lineSeries0.replace(1, newPoint.x, 0) + } + + if (newPoint.x <= xAxis.max.getTime() || logsModelNg.busy) { + return; + } + + var diffMaxToNew = newPoint.x - xAxis.max.getTime(); + print("diffToNew is", diffMaxToNew) + if (diffMaxToNew < 1000 * 60 * 5) { + chartView.animationOptions = ChartView.NoAnimation + var newMin = xAxis.min.getTime() + diffMaxToNew; + xAxis.max = new Date(newPoint.x); + xAxis.min = new Date(newMin) + chartView.animationOptions = ChartView.SeriesAnimations + } + } } color: Qt.rgba(root.color.r, root.color.g, root.color.b, .3) onHovered: { @@ -268,9 +303,13 @@ Item { MouseArea { - anchors.fill: parent + x: chartView.plotArea.x + y: chartView.plotArea.y + width: chartView.plotArea.width + height: chartView.plotArea.height property int lastX: 0 property int lastY: 0 + preventStealing: false function scrollRightLimited(dx) { chartView.animationOptions = ChartView.NoAnimation @@ -303,7 +342,7 @@ Item { lastY = mouse.y } onClicked: { - var pt = chartView.mapToValue(Qt.point(mouse.x, mouse.y), mainSeries) + var pt = chartView.mapToValue(Qt.point(mouse.x + chartView.plotArea.x, mouse.y + chartView.plotArea.y), mainSeries) mainSeries.markClosestPoint(pt) } diff --git a/nymea-app/ui/customviews/SensorChart.qml b/nymea-app/ui/customviews/SensorChart.qml deleted file mode 100644 index d2cadaf8..00000000 --- a/nymea-app/ui/customviews/SensorChart.qml +++ /dev/null @@ -1,28 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 2.1 -import QtQuick.Controls.Material 2.1 -import QtQuick.Layouts 1.3 -import "../components" -import Nymea 1.0 - -CustomViewBase { - id: root - implicitHeight: width * .6 - property string interfaceName - - readonly property string stateTypeName: { - switch (interfaceName) { - case "lightsensor": - return "lightIntensity"; - default: - return interfaceName.replace("sensor", ""); - } - } - GenericTypeGraph { - anchors { left: parent.left; top: parent.top; right: parent.right; bottom: parent.bottom } - device: root.device - stateType: root.deviceClass.stateTypes.findByName(root.stateTypeName) - color: app.interfaceToColor(root.interfaceName) - iconSource: app.interfaceToIcon(root.interfaceName) - } -} diff --git a/nymea-app/ui/devicepages/SensorDevicePagePost110.qml b/nymea-app/ui/devicepages/SensorDevicePagePost110.qml index 135bef73..bd4330df 100644 --- a/nymea-app/ui/devicepages/SensorDevicePagePost110.qml +++ b/nymea-app/ui/devicepages/SensorDevicePagePost110.qml @@ -18,10 +18,21 @@ ListView { } } } - delegate: SensorChart { + delegate: GenericTypeGraph { width: parent.width - interfaceName: modelData device: root.device - deviceClass: root.deviceClass + stateType: root.deviceClass.stateTypes.findByName(stateTypeName) + color: app.interfaceToColor(modelData) + iconSource: app.interfaceToIcon(modelData) + + implicitHeight: width * .6 + property string stateTypeName: { + switch (modelData) { + case "lightsensor": + return "lightIntensity"; + default: + return modelData.replace("sensor", ""); + } + } } } diff --git a/nymea-app/ui/devicepages/SmartMeterDevicePage.qml b/nymea-app/ui/devicepages/SmartMeterDevicePage.qml new file mode 100644 index 00000000..961f7dee --- /dev/null +++ b/nymea-app/ui/devicepages/SmartMeterDevicePage.qml @@ -0,0 +1,44 @@ +import QtQuick 2.5 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.1 +import Nymea 1.0 +import "../components" +import "../customviews" + +DevicePageBase { + id: root + + ListView { + anchors { fill: parent } + model: ListModel { + Component.onCompleted: { + if (root.deviceClass.interfaces.indexOf("extendedsmartmeterproducer") >= 0 + || root.deviceClass.interfaces.indexOf("extendedsmartmeterconsumer") >= 0) { + append( {interface: "extendedsmartmeterproducer", stateTypeName: "currentPower" }) + } + if (root.deviceClass.interfaces.indexOf("smartmeterproducer") >= 0) { + append( {interface: "smartmeterproducer", stateTypeName: "totalEnergyProduced" }) + } + if (root.deviceClass.interfaces.indexOf("smartmeterconsumer") >= 0) { + append( {interface: "smartmeterconsumer", stateTypeName: "totalEnergyConsumed" }) + } + print("shown graphs are", count) + } + } + delegate: ColumnLayout { + width: parent.width + Label { + Layout.fillWidth: true + Layout.leftMargin: app.margins; Layout.topMargin: app.margins; Layout.rightMargin: app.rightMargins; + text: root.deviceClass.stateTypes.findByName(model.stateTypeName).displayName + } + GenericTypeGraph { + Layout.fillWidth: true + device: root.device + stateType: root.deviceClass.stateTypes.findByName(model.stateTypeName) + color: app.interfaceToColor(model.interface) + iconSource: app.interfaceToIcon(model.interface) + } + } + } +} diff --git a/nymea-app/ui/images/smartmeter.svg b/nymea-app/ui/images/smartmeter.svg new file mode 100644 index 00000000..4bd777f8 --- /dev/null +++ b/nymea-app/ui/images/smartmeter.svg @@ -0,0 +1,202 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +