diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 3141da19..c9f5fad0 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -281,5 +281,11 @@ ui/mainviews/energy/PowerBalanceHistory.qml ui/mainviews/energy/CurrentPowerBalancePieChart.qml ui/components/MultiSelectionTabs.qml + ui/mainviews/energy/PowerBalanceHistoryPage.qml + ui/mainviews/energy/CurrentPowerBalancePage.qml + ui/mainviews/energy/PowerBalanceStatsPage.qml + ui/mainviews/energy/ConsumersHistoryPage.qml + ui/mainviews/energy/ConsumerStatsPage.qml + ui/mainviews/energy/ConsumersPieChartPage.qml diff --git a/nymea-app/ui/Nymea.qml b/nymea-app/ui/Nymea.qml index b8abf74d..d1b05506 100644 --- a/nymea-app/ui/Nymea.qml +++ b/nymea-app/ui/Nymea.qml @@ -142,7 +142,7 @@ ApplicationWindow { property NymeaDiscovery nymeaDiscovery: NymeaDiscovery { objectName: "discovery" awsClient: AWSClient - bluetoothDiscoveryEnabled: PlatformPermissions.bluetoothPermission === PlatformPermissions.PermissionStatusGranted + bluetoothDiscoveryEnabled: false// PlatformPermissions.bluetoothPermission === PlatformPermissions.PermissionStatusGranted } property var supportedInterfaces: [ diff --git a/nymea-app/ui/mainviews/EnergyView.qml b/nymea-app/ui/mainviews/EnergyView.qml index 3effb62d..28217441 100644 --- a/nymea-app/ui/mainviews/EnergyView.qml +++ b/nymea-app/ui/mainviews/EnergyView.qml @@ -60,8 +60,6 @@ MainViewBase { engine: _engine } - property var thingColors: [Style.blue, Style.green, Style.red, Style.yellow, Style.purple, Style.orange, Style.lime, Style.pink, Style.darkBlue] - ThingsProxy { id: energyMeters @@ -169,7 +167,6 @@ MainViewBase { Layout.preferredHeight: width energyManager: energyManager visible: consumers.count > 0 - colors: root.thingColors consumers: consumers animationsEnabled: Qt.application.active && root.isCurrentItem && flickable.contentY < y + height && flickable.contentY + flickable.height > y onAnimationsEnabledChanged: print("animations for consumer balance chart", animationsEnabled ? "enabled" : "disabled") @@ -180,7 +177,7 @@ MainViewBase { Layout.fillWidth: true Layout.preferredHeight: width visible: consumers.count > 0 - colors: root.thingColors + energyManager: energyManager consumers: consumers } @@ -189,7 +186,6 @@ MainViewBase { Layout.preferredHeight: width energyManager: energyManager visible: consumers.count > 0 - colors: root.thingColors consumers: consumers } } diff --git a/nymea-app/ui/mainviews/energy/ConsumerStats.qml b/nymea-app/ui/mainviews/energy/ConsumerStats.qml index 2e73eec1..24313f31 100644 --- a/nymea-app/ui/mainviews/energy/ConsumerStats.qml +++ b/nymea-app/ui/mainviews/energy/ConsumerStats.qml @@ -9,9 +9,8 @@ StatsBase { id: root property EnergyManager energyManager: null - property var colors: null - property ThingsProxy consumers: null + property bool titleVisible: true QtObject { id: d @@ -20,6 +19,8 @@ StatsBase { property int startOffset: 0 + property Thing selectedThing: null + property date startTime: root.calculateTimestamp(config.startTime(), config.sampleRate, startOffset) property date endTime: root.calculateTimestamp(config.startTime(), config.sampleRate, startOffset + config.count) @@ -45,6 +46,14 @@ StatsBase { consumersRepeater.itemAt(i).refresh() } } + + function selectThing(thing) { + if (d.selectedThing === thing) { + d.selectedThing = null + } else { + d.selectedThing = thing + } + } } ThingPowerLogsLoader { @@ -146,9 +155,11 @@ StatsBase { } barSet = barSeries.append(consumerDelegate.thing.name, values) - barSet.color = NymeaUtils.generateColor(Style.generationBaseColor, index) - barSet.borderColor = barSet.color - barSet.borderWith = 0 + barSet.color = Qt.binding(function() { + return NymeaUtils.generateColor(Style.generationBaseColor, index, d.selectedThing == null || consumerDelegate.thing == d.selectedThing ? 1 : 0.3) + }) + barSet.borderColor = Qt.binding(function(){ return barSet.color}) + barSet.borderWidth = 0 } Component.onDestruction: { barSeries.remove(barSet) @@ -160,12 +171,17 @@ StatsBase { anchors.fill: parent spacing: 0 -// Label { -// Layout.fillWidth: true -// Layout.margins: Style.smallMargins -// horizontalAlignment: Text.AlignHCenter -// text: qsTr("Consumers totals") -// } + Label { + Layout.fillWidth: true + Layout.margins: Style.smallMargins + horizontalAlignment: Text.AlignHCenter + text: qsTr("Consumers totals") + visible: root.titleVisible + MouseArea { + anchors.fill: parent + onClicked: pageStack.push(Qt.resolvedUrl("ConsumerStatsPage.qml"), {energyManager: root.energyManager, consumers: root.consumers}) + } + } SelectionTabs { id: selectionTabs @@ -311,6 +327,7 @@ StatsBase { } RowLayout { + id: legend anchors { left: parent.left; bottom: parent.bottom; right: parent.right } anchors.leftMargin: chartView.plotArea.x height: Style.smallIconSize @@ -318,17 +335,30 @@ StatsBase { Repeater { model: root.consumers - delegate: Item { + delegate: MouseArea { id: legendDelegate Layout.fillWidth: true Layout.fillHeight: true readonly property Thing thing: root.consumers.get(index) - ColorIcon { - name: app.interfacesToIcon(legendDelegate.thing.thingClass.interfaces) - size: Style.smallIconSize - color: index >= 0 ? NymeaUtils.generateColor(Style.generationBaseColor, index) : "white" + onClicked: d.selectThing(thing) + Row { anchors.centerIn: parent + spacing: Style.smallMargins + ColorIcon { + name: app.interfacesToIcon(legendDelegate.thing.thingClass.interfaces) + size: Style.smallIconSize + color: index >= 0 ? NymeaUtils.generateColor(Style.generationBaseColor, index) : "white" + } + Label { + text: legendDelegate.thing.name + width: Math.max(0, legendDelegate.width - x) + anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight + font: Style.smallFont + visible: legend.width / root.consumers.count >= 80 + } } + } } } @@ -496,7 +526,7 @@ StatsBase { var consumerDelegate = consumersRepeater.itemAt(i) var consumer = consumerDelegate.thing var entry = { - name: consumer.name, + consumer: consumer, value: consumersRepeater.itemAt(i).barSet.at(toolTip.idx).toFixed(2), indexInModel: i } @@ -518,14 +548,14 @@ StatsBase { } delegate: RowLayout { + opacity: d.selectedThing == null || d.selectedThing === model.consumer ? 1 : 0.3 Rectangle { width: Style.extraSmallFont.pixelSize height: width - // color: root.colors[model.indexInModel % root.colors.length] color: NymeaUtils.generateColor(Style.generationBaseColor, model.indexInModel) } Label { - text: "%1: %2 kWh".arg(model.name).arg(model.value) + text: "%1: %2 kWh".arg(model.consumer.name).arg(model.value) font: Style.extraSmallFont } } diff --git a/nymea-app/ui/mainviews/energy/ConsumerStatsPage.qml b/nymea-app/ui/mainviews/energy/ConsumerStatsPage.qml new file mode 100644 index 00000000..1853812f --- /dev/null +++ b/nymea-app/ui/mainviews/energy/ConsumerStatsPage.qml @@ -0,0 +1,24 @@ +import QtQuick 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import Nymea 1.0 +import "qrc:/ui/components" + +Page { + id: root + + property alias energyManager: consumersStats + property alias consumers: consumersStats.consumers + + header: NymeaHeader { + text: qsTr("Consumers balance") + backButtonVisible: true + onBackPressed: pageStack.pop() + } + + ConsumerStats { + id: consumersStats + anchors.fill: parent + titleVisible: false + } +} diff --git a/nymea-app/ui/mainviews/energy/ConsumersHistory.qml b/nymea-app/ui/mainviews/energy/ConsumersHistory.qml index 68b051b7..1f5f38f5 100644 --- a/nymea-app/ui/mainviews/energy/ConsumersHistory.qml +++ b/nymea-app/ui/mainviews/energy/ConsumersHistory.qml @@ -8,8 +8,9 @@ import "qrc:/ui/components" Item { id: root - property var colors: null + property EnergyManager energyManager: null property ThingsProxy consumers: null + property bool titleVisible: true PowerBalanceLogs { id: powerBalanceLogs @@ -53,6 +54,8 @@ Item { property date now: new Date() + property var selectedSeries: null + readonly property int range: selectionTabs.currentValue.range readonly property int sampleRate: selectionTabs.currentValue.sampleRate readonly property int visibleValues: range / sampleRate @@ -95,6 +98,15 @@ Item { logsLoader.fetchLogs(); } } + + function selectSeries(series) { + print("selecting series", series) + if (d.selectedSeries === series) { + d.selectedSeries = null + } else { + d.selectedSeries = series + } + } } Connections { @@ -111,12 +123,17 @@ Item { anchors.fill: parent spacing: 0 -// Label { -// Layout.fillWidth: true -// Layout.margins: Style.smallMargins -// horizontalAlignment: Text.AlignHCenter -// text: qsTr("Consumers history") -// } + Label { + Layout.fillWidth: true + Layout.margins: Style.smallMargins + horizontalAlignment: Text.AlignHCenter + text: qsTr("Consumers history") + visible: root.titleVisible + MouseArea { + anchors.fill: parent + onClicked: pageStack.push(Qt.resolvedUrl("ConsumersHistoryPage.qml"), {energyManager: root.energyManager, consumers: root.consumers}) + } + } SelectionTabs { id: selectionTabs @@ -456,8 +473,11 @@ Item { series.lowerSeries = lineSeriesComponent.createObject(series) series.upperSeries = lineSeriesComponent.createObject(series) series.color = NymeaUtils.generateColor(Style.generationBaseColor, index) + series.opacity = Qt.binding(function() { + return d.selectedSeries == null || d.selectedSeries == series ? 1 : 0.3 + }) series.borderWidth = 0; - series.borderColor = series.color + series.borderColor = series.color // Add a first point at 0 value series.lowerSeries.insert(0, new Date().getTime(), 0) @@ -472,6 +492,7 @@ Item { } RowLayout { + id: legend anchors { left: parent.left; bottom: parent.bottom; right: parent.right } anchors.leftMargin: chartView.plotArea.x height: Style.smallIconSize @@ -479,23 +500,34 @@ Item { Repeater { model: root.consumers - delegate: Item { + delegate: MouseArea { id: legendDelegate Layout.fillWidth: true Layout.fillHeight: true readonly property Thing thing: root.consumers.get(index) - ColorIcon { - name: app.interfacesToIcon(legendDelegate.thing.thingClass.interfaces) - size: Style.smallIconSize - color: index >= 0 ? NymeaUtils.generateColor(Style.generationBaseColor, index) : "white" + onClicked: d.selectSeries(consumersRepeater.itemAt(index).series) + opacity: d.selectedSeries == null || d.selectedSeries === consumersRepeater.itemAt(index).series ? 1 : 0.3 + Row { anchors.centerIn: parent + spacing: Style.smallMargins + ColorIcon { + name: app.interfacesToIcon(legendDelegate.thing.thingClass.interfaces) + size: Style.smallIconSize + color: index >= 0 ? NymeaUtils.generateColor(Style.generationBaseColor, index) : "white" + } + Label { + text: legendDelegate.thing.name + width: Math.max(0, legendDelegate.width - x) + anchors.verticalCenter: parent.verticalCenter + elide: Text.ElideRight + font: Style.smallFont + visible: legend.width / root.consumers.count >= 80 + } } } } - } - MouseArea { id: mouseArea anchors.fill: parent @@ -665,20 +697,21 @@ Item { Repeater { model: consumersRepeater.count delegate: RowLayout { + readonly property Item chartItem: consumersRepeater.itemAt(index) id: consumerToolTipDelegate + opacity: d.selectedSeries == null || d.selectedSeries === chartItem.series ? 1 : 0.3 Rectangle { width: Style.extraSmallFont.pixelSize height: width - // color: index >= 0 ? root.colors[index % root.colors.length] : "white" color: index >= 0 ? NymeaUtils.generateColor(Style.generationBaseColor, index) : "white" } Label { - property ThingPowerLogEntry entry: toolTip.idx >= 0 ? consumersRepeater.itemAt(index).logs.find(toolTip.timestamp) : null + property ThingPowerLogEntry entry: toolTip.idx >= 0 ? chartItem.logs.find(toolTip.timestamp) : null property double rawValue: entry ? entry.currentPower : 0 property double displayValue: rawValue >= 1000 ? rawValue / 1000 : rawValue property string unit: rawValue >= 1000 ? "kW" : "W" - text: "%1: %2 %3".arg(consumersRepeater.itemAt(index).thing.name).arg(displayValue.toFixed(2)).arg(unit) + text: "%1: %2 %3".arg(chartItem.thing.name).arg(displayValue.toFixed(2)).arg(unit) font: Style.extraSmallFont } } diff --git a/nymea-app/ui/mainviews/energy/ConsumersHistoryPage.qml b/nymea-app/ui/mainviews/energy/ConsumersHistoryPage.qml new file mode 100644 index 00000000..5edf087d --- /dev/null +++ b/nymea-app/ui/mainviews/energy/ConsumersHistoryPage.qml @@ -0,0 +1,24 @@ +import QtQuick 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import Nymea 1.0 +import "qrc:/ui/components" + +Page { + id: root + + property alias energyManager: consumersHistory + property alias consumers: consumersHistory.consumers + + header: NymeaHeader { + text: qsTr("Power balance totals") + backButtonVisible: true + onBackPressed: pageStack.pop() + } + + ConsumersHistory { + id: consumersHistory + anchors.fill: parent + titleVisible: false + } +} diff --git a/nymea-app/ui/mainviews/energy/ConsumersPieChart.qml b/nymea-app/ui/mainviews/energy/ConsumersPieChart.qml index ef8cffe2..fc42cb06 100644 --- a/nymea-app/ui/mainviews/energy/ConsumersPieChart.qml +++ b/nymea-app/ui/mainviews/energy/ConsumersPieChart.qml @@ -7,23 +7,13 @@ import QtCharts 2.2 import Nymea 1.0 import "qrc:/ui/components" -ChartView { +Item { id: root - backgroundColor: "transparent" - animationOptions: animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation -// title: qsTr("Consumers balance") - titleColor: Style.foregroundColor - legend.visible: false - - margins.left: 0 - margins.right: 0 - margins.bottom: 0 - margins.top: 0 property EnergyManager energyManager: null property ThingsProxy consumers: null - property var colors: null property bool animationsEnabled: true + property bool titleVisible: true readonly property Thing rootMeter: engine.thingManager.fetchingData ? null : engine.thingManager.things.getThing(energyManager.rootMeterId) onRootMeterChanged: updateConsumers() @@ -72,7 +62,7 @@ ChartView { } function updateConsumers() { - root.animationOptions = ChartView.NoAnimation + chart.animationOptions = ChartView.NoAnimation consumersBalanceSeries.clear(); d.unknownSlice = null d.idleSlice = null @@ -115,131 +105,164 @@ ChartView { d.thingsColorMap = colorMap - root.animationOptions = Qt.binding(function() { + chart.animationOptions = Qt.binding(function() { return root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation }) } - PieSeries { - id: consumersBalanceSeries - size: 0.88 - holeSize: 0.7 + Label { + id: titleLabel + anchors { left: parent.left; top: parent.top; right: parent.right; margins: Style.smallMargins } + horizontalAlignment: Text.AlignHCenter + text: qsTr("Consumers balance") + visible: root.titleVisible + MouseArea { + anchors.fill: parent + onClicked: { + pageStack.push(Qt.resolvedUrl("ConsumersPieChartPage.qml"), {energyManager: root.energyManager, consumers: root.consumers}) + } + } } - Flickable { - id: centerLayout - x: root.plotArea.x + (root.plotArea.width - width) / 2 - y: root.plotArea.y + (root.plotArea.height - height) / 2 - width: Math.min(root.plotArea.width, root.plotArea.width) * 0.65 - height: Math.min(contentColumn.height + topMargin + bottomMargin, width) - topMargin: Style.smallIconSize - bottomMargin: Style.smallIconSize - opacity: 0 -// property int maximumHeight: root.plotArea.height * 0.65 - contentHeight: contentColumn.implicitHeight + ChartView { + id: chart - ColumnLayout { - id: contentColumn - width: parent.width - spacing: Style.smallMargins + anchors { left: parent.left; right: parent.right; bottom: parent.bottom; top: titleLabel.bottom} + + backgroundColor: "transparent" + animationOptions: animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation + titleColor: Style.foregroundColor + legend.visible: false + + margins.left: 0 + margins.right: 0 + margins.bottom: 0 + margins.top: 0 + + + PieSeries { + id: consumersBalanceSeries + size: 0.88 + holeSize: 0.7 + } + + Flickable { + id: centerLayout + x: chart.plotArea.x + (chart.plotArea.width - width) / 2 + y: chart.plotArea.y + (chart.plotArea.height - height) / 2 + width: Math.min(chart.plotArea.width, chart.plotArea.width) * 0.65 + height: Math.min(contentColumn.height + topMargin + bottomMargin, width) + topMargin: Style.smallIconSize + bottomMargin: Style.smallIconSize + opacity: 0 + // property int maximumHeight: chart.plotArea.height * 0.65 + + contentHeight: contentColumn.implicitHeight ColumnLayout { - Layout.fillWidth: true - spacing: 0 - visible: root.rootMeter - Label { - text: qsTr("Total") - font: Style.smallFont - Layout.topMargin: Style.smallMargins + id: contentColumn + width: parent.width + spacing: Style.smallMargins + + ColumnLayout { Layout.fillWidth: true - horizontalAlignment: Text.AlignHCenter - } - - Label { - // We're using the maximum value of the energy managers consumption, the sum of all consumers because: - // * in a standard setup, the energy manager would know everything and the consumption will always be greater than the sum of all individual consumers - // * if there is a producer which is unknown to nymea though, it will decrease the consumption on the root meter so it may be smaller than the - // summation of all consumers. In this particular chart that would be nonsense so in the end we'll only lose the "unknown" power consumption in such a setup - property double finalTotal: Math.max(energyManager.currentPowerConsumption, d.consumersSummation) - text: "%1 %2" - .arg((finalTotal / (finalTotal > 1000 ? 1000 : 1)).toFixed(1)) - .arg(finalTotal > 1000 ? "kW" : "W") - Layout.fillWidth: true - horizontalAlignment: Text.AlignHCenter - font: Style.bigFont - } - } - - Repeater { - model: ThingsProxy { - id: sortedConsumers - engine: _engine - parentProxy: root.consumers - sortStateName: "currentPower" - sortOrder: Qt.DescendingOrder - } - - delegate: ColumnLayout { - id: consumerDelegate - width: parent ? parent.width : 0 spacing: 0 - property Thing consumer: consumers.getThing(model.id) - property State currentPowerState: consumer ? consumer.stateByName("currentPower") : null - property double value: currentPowerState ? currentPowerState.value : 0 + visible: root.rootMeter + Label { + text: qsTr("Total") + font: Style.smallFont + Layout.topMargin: Style.smallMargins + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + } Label { - text: model.name - Layout.fillWidth: true - horizontalAlignment: Text.AlignHCenter - font: Style.extraSmallFont - } - Label { - color: d.thingsColorMap.hasOwnProperty(consumer) ? d.thingsColorMap[consumer] : "transparent" + // We're using the maximum value of the energy managers consumption, the sum of all consumers because: + // * in a standard setup, the energy manager would know everything and the consumption will always be greater than the sum of all individual consumers + // * if there is a producer which is unknown to nymea though, it will decrease the consumption on the root meter so it may be smaller than the + // summation of all consumers. In this particular chart that would be nonsense so in the end we'll only lose the "unknown" power consumption in such a setup + property double finalTotal: Math.max(energyManager.currentPowerConsumption, d.consumersSummation) text: "%1 %2" - .arg((consumerDelegate.value / (consumerDelegate.value > 1000 ? 1000 : 1)).toFixed(1)) - .arg(consumerDelegate.value > 1000 ? "kW" : "W") + .arg((finalTotal / (finalTotal > 1000 ? 1000 : 1)).toFixed(1)) + .arg(finalTotal > 1000 ? "kW" : "W") Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - font: Style.smallFont + font: Style.bigFont + } + } + + Repeater { + model: ThingsProxy { + id: sortedConsumers + engine: _engine + parentProxy: root.consumers + sortStateName: "currentPower" + sortOrder: Qt.DescendingOrder + } + + delegate: ColumnLayout { + id: consumerDelegate + width: parent ? parent.width : 0 + spacing: 0 + property Thing consumer: consumers.getThing(model.id) + property State currentPowerState: consumer ? consumer.stateByName("currentPower") : null + property double value: currentPowerState ? currentPowerState.value : 0 + + Label { + text: model.name + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + font: Style.extraSmallFont + } + Label { + color: d.thingsColorMap.hasOwnProperty(consumer) ? d.thingsColorMap[consumer] : "transparent" + text: "%1 %2" + .arg((consumerDelegate.value / (consumerDelegate.value > 1000 ? 1000 : 1)).toFixed(1)) + .arg(consumerDelegate.value > 1000 ? "kW" : "W") + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + font: Style.smallFont + } } } } + } - } - - Rectangle { - id: innerMask - anchors.fill: centerLayout - radius: width / 2 - visible: false - gradient: Gradient { - GradientStop { position: 0; color: "transparent" } - GradientStop { position: 1-(centerLayout.height - downArrow.height * 1.5) / centerLayout.height; color: "red" } - GradientStop { position: (centerLayout.height - downArrow.height * 1.5) / centerLayout.height; color: "red" } - GradientStop { position: 1; color: "transparent" } + Rectangle { + id: innerMask + anchors.fill: centerLayout + radius: width / 2 + visible: false + gradient: Gradient { + GradientStop { position: 0; color: "transparent" } + GradientStop { position: 1-(centerLayout.height - downArrow.height * 1.5) / centerLayout.height; color: "red" } + GradientStop { position: (centerLayout.height - downArrow.height * 1.5) / centerLayout.height; color: "red" } + GradientStop { position: 1; color: "transparent" } + } } - } - OpacityMask { - anchors.fill: centerLayout - source: centerLayout - maskSource: innerMask - } + OpacityMask { + anchors.fill: centerLayout + source: centerLayout + maskSource: innerMask + } - ColorIcon { - id: upArrow - anchors { top: centerLayout.top; horizontalCenter: centerLayout.horizontalCenter } - size: Style.smallIconSize - name: "up" - visible: !centerLayout.atYBeginning - } - ColorIcon { - id: downArrow - anchors { bottom: centerLayout.bottom; horizontalCenter: centerLayout.horizontalCenter } - size: Style.smallIconSize - name: "down" - visible: !centerLayout.atYEnd + ColorIcon { + id: upArrow + anchors { top: centerLayout.top; horizontalCenter: centerLayout.horizontalCenter } + size: Style.smallIconSize + name: "up" + visible: !centerLayout.atYBeginning + } + ColorIcon { + id: downArrow + anchors { bottom: centerLayout.bottom; horizontalCenter: centerLayout.horizontalCenter } + size: Style.smallIconSize + name: "down" + visible: !centerLayout.atYEnd + } } } + diff --git a/nymea-app/ui/mainviews/energy/ConsumersPieChartPage.qml b/nymea-app/ui/mainviews/energy/ConsumersPieChartPage.qml new file mode 100644 index 00000000..f2b9889f --- /dev/null +++ b/nymea-app/ui/mainviews/energy/ConsumersPieChartPage.qml @@ -0,0 +1,24 @@ +import QtQuick 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import Nymea 1.0 +import "qrc:/ui/components" + +Page { + id: root + + property alias energyManager: consumersPieChart.energyManager + property alias consumers: consumersPieChart.consumers + + header: NymeaHeader { + text: qsTr("Consumers balance") + backButtonVisible: true + onBackPressed: pageStack.pop() + } + + ConsumersPieChart { + id: consumersPieChart + anchors.fill: parent + titleVisible: false + } +} diff --git a/nymea-app/ui/mainviews/energy/CurrentConsumptionBalancePieChart.qml b/nymea-app/ui/mainviews/energy/CurrentConsumptionBalancePieChart.qml index 80a4440c..0eae0b52 100644 --- a/nymea-app/ui/mainviews/energy/CurrentConsumptionBalancePieChart.qml +++ b/nymea-app/ui/mainviews/energy/CurrentConsumptionBalancePieChart.qml @@ -10,7 +10,7 @@ ChartView { id: consumptionPieChart backgroundColor: "transparent" animationOptions: animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation - title: qsTr("My energy mix") + title: qsTr("My energy consumption") titleColor: Style.foregroundColor legend.visible: false diff --git a/nymea-app/ui/mainviews/energy/CurrentPowerBalancePage.qml b/nymea-app/ui/mainviews/energy/CurrentPowerBalancePage.qml new file mode 100644 index 00000000..768e5cba --- /dev/null +++ b/nymea-app/ui/mainviews/energy/CurrentPowerBalancePage.qml @@ -0,0 +1,41 @@ +import QtQuick 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import Nymea 1.0 +import "qrc:/ui/components" + +Page { + id: root + + property EnergyManager energyManager: null + property ThingsProxy consumers: null + property ThingsProxy producers: null + + header: NymeaHeader { + text: qsTr("My energy mix") + backButtonVisible: true + onBackPressed: pageStack.pop() + } + + GridLayout { + anchors.fill: parent + columns: app.landscape ? 2 : 1 + + CurrentConsumptionBalancePieChart { + Layout.fillWidth: true + Layout.fillHeight: true + energyManager: root.energyManager + visible: root.producers.count > 0 + animationsEnabled: Qt.application.active + } + CurrentProductionBalancePieChart { + Layout.fillWidth: true + Layout.fillHeight: true + energyManager: root.energyManager + visible: root.producers.count > 0 + animationsEnabled: Qt.application.active + } + } + + +} diff --git a/nymea-app/ui/mainviews/energy/CurrentPowerBalancePieChart.qml b/nymea-app/ui/mainviews/energy/CurrentPowerBalancePieChart.qml index df7a5224..70fb1e20 100644 --- a/nymea-app/ui/mainviews/energy/CurrentPowerBalancePieChart.qml +++ b/nymea-app/ui/mainviews/energy/CurrentPowerBalancePieChart.qml @@ -19,6 +19,20 @@ Item { readonly property double fromProduction: energyManager.currentPowerConsumption - fromGrid - fromStorage readonly property double toGrid: Math.max(0, - energyManager.currentPowerAcquisition) + + Label { + id: titleLabel + anchors { left: parent.left; top: parent.top; right: parent.right; margins: Style.smallMargins } + horizontalAlignment: Text.AlignHCenter + text: qsTr("My energy mix") + MouseArea { + anchors.fill: parent + onClicked: { + pageStack.push(Qt.resolvedUrl("CurrentPowerBalancePage.qml"), {energyManager: root.energyManager}) + } + } + } + QtObject { id: d function formatValue(value) { @@ -37,11 +51,11 @@ Item { property int chartSize: width / 2.5 property point acquisitionPos: Qt.point(chartSize/2 + Style.margins, chartSize/2 + Style.margins) - property point productionPos: Qt.point(root.width - (chartSize/2 + Style.margins), chartSize/2 + Style.margins) - property point storagePos: Qt.point(chartSize/2 + Style.margins, root.height - (chartSize/2 + Style.margins)) + property point productionPos: Qt.point(contentContainer.width - (chartSize/2 + Style.margins), chartSize/2 + Style.margins) + property point storagePos: Qt.point(chartSize/2 + Style.margins, contentContainer.height - (chartSize/2 + Style.margins)) property point consumptionPos: batteries.count > 0 || producers.count === 0 - ? Qt.point(root.width - (chartSize/2 + Style.margins), root.height - (chartSize/2 + Style.margins)) - : Qt.point(root.width / 2, root.height - (chartSize/2 + Style.margins)) + ? Qt.point(contentContainer.width - (chartSize/2 + Style.margins), contentContainer.height - (chartSize/2 + Style.margins)) + : Qt.point(contentContainer.width / 2, contentContainer.height - (chartSize/2 + Style.margins)) } ThingsProxy { @@ -66,805 +80,431 @@ Item { duration: 5000 } - Canvas { - id: canvas - anchors.fill: parent + Item { + id: contentContainer + anchors { left: parent.left; right: parent.right; bottom: parent.bottom; top: titleLabel.bottom} - renderTarget: Canvas.FramebufferObject - renderStrategy: Canvas.Cooperative + Canvas { + id: canvas + anchors.fill: parent - onPaint: { - var ctx = getContext("2d"); + renderTarget: Canvas.FramebufferObject + renderStrategy: Canvas.Cooperative - var solarPos = Qt.point(d.productionPos.x - width / 2, d.productionPos.y - height / 2) - var storagePos = Qt.point(d.storagePos.x - width / 2, d.storagePos.y - width / 2) - var consumptionPos = Qt.point(d.consumptionPos.x - width / 2, d.consumptionPos.y - height / 2) - var gridPos = Qt.point(d.acquisitionPos.x - width / 2, d.acquisitionPos.y - height / 2) + onPaint: { + var ctx = getContext("2d"); - ctx.save(); - ctx.reset() + var solarPos = Qt.point(d.productionPos.x - width / 2, d.productionPos.y - height / 2) + var storagePos = Qt.point(d.storagePos.x - width / 2, d.storagePos.y - width / 2) + var consumptionPos = Qt.point(d.consumptionPos.x - width / 2, d.consumptionPos.y - height / 2) + var gridPos = Qt.point(d.acquisitionPos.x - width / 2, d.acquisitionPos.y - height / 2) - ctx.translate(width / 2, height / 2); + ctx.save(); + ctx.reset() - ctx.strokeStyle = Style.foregroundColor - ctx.fillStyle = Style.foregroundColor - ctx.lineWidth = 2 + ctx.translate(width / 2, height / 2); - var biggest = Math.max( - Math.abs(energyManager.currentPowerAcquisition), - Math.abs(energyManager.currentPowerConsumption), - Math.abs(energyManager.currentPowerProduction), - Math.abs(energyManager.currentPowerStorage) - ) - var size + ctx.strokeStyle = Style.foregroundColor + ctx.fillStyle = Style.foregroundColor + ctx.lineWidth = 2 + + var biggest = Math.max( + Math.abs(energyManager.currentPowerAcquisition), + Math.abs(energyManager.currentPowerConsumption), + Math.abs(energyManager.currentPowerProduction), + Math.abs(energyManager.currentPowerStorage) + ) + var size - if (root.toGrid > 0) { - size = root.toGrid / biggest - drawDottedCurve(ctx, solarPos, gridPos, size, Style.yellow) - } + if (root.toGrid > 0) { + size = root.toGrid / biggest + drawDottedCurve(ctx, solarPos, gridPos, size, Style.yellow) + } - if (energyManager.currentPowerProduction < 0 && root.fromProduction) { - size = root.fromProduction / biggest - drawDottedCurve(ctx, solarPos, consumptionPos, size, Style.green) - } + if (energyManager.currentPowerProduction < 0 && root.fromProduction) { + size = root.fromProduction / biggest + drawDottedCurve(ctx, solarPos, consumptionPos, size, Style.green) + } - if (batteries.count > 0) { - if (energyManager.currentPowerStorage > 0) { - if (energyManager.currentPowerProduction < 0) { + if (batteries.count > 0) { + if (energyManager.currentPowerStorage > 0) { + if (energyManager.currentPowerProduction < 0) { + size = Math.abs(energyManager.currentPowerStorage) / biggest + drawDottedCurve(ctx, solarPos, storagePos, size, Style.purple) + } else { + size = Math.abs(energyManager.currentPowerStorage) / biggest + drawDottedCurve(ctx, gridPos, storagePos, size, Style.purple) + } + } + + if (energyManager.currentPowerStorage < 0) { size = Math.abs(energyManager.currentPowerStorage) / biggest - drawDottedCurve(ctx, solarPos, storagePos, size, Style.purple) - } else { - size = Math.abs(energyManager.currentPowerStorage) / biggest - drawDottedCurve(ctx, gridPos, storagePos, size, Style.purple) + drawDottedCurve(ctx, storagePos, consumptionPos, size, Style.orange) } } - if (energyManager.currentPowerStorage < 0) { - size = Math.abs(energyManager.currentPowerStorage) / biggest - drawDottedCurve(ctx, storagePos, consumptionPos, size, Style.orange) + if (energyManager.currentPowerAcquisition > 0) { + size = Math.abs(energyManager.currentPowerAcquisition) / biggest + drawDottedCurve(ctx, gridPos, consumptionPos, size, Style.red) + } + + ctx.restore(); + } + + function bezierCurvePoint(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, t) { + var x = Math.pow(1-t, 3)*p0x + 3*Math.pow(1-t, 2)*t*p1x + 3*(1-t)*Math.pow(t, 2)*p2x + Math.pow(t, 3)*p3x; + var y = Math.pow(1-t, 3)*p0y + 3*Math.pow(1-t, 2)*t*p1y + 3*(1-t)*Math.pow(t, 2)*p2y + Math.pow(t, 3)*p3y; + return Qt.point(x, y) + } + + function circlePoint(center, radius, angle) { + var x = center.x + radius * Math.cos(angle * 2 * Math.PI / 360) + var y = center.y + radius * Math.sin(angle * 2 * Math.PI / 360) + return Qt.point(x, y) + } + + function drawDottedCurve(ctx, start, end, size, color) { + var c1 = getControlPoint(start) + var c2 = getControlPoint(end) + ctx.fillStyle = color + ctx.strokeStyle = color + var count = 10; + for (var i = 1; i <= count; i++) { + var offset = 1 / count; + var progress = d.progress + i * offset + if (progress > 1) + progress -= 1 + var point = bezierCurvePoint(start.x, start.y, c1.x, c1.y, c2.x, c2.y, end.x, end.y, progress) + // print("painting", d.progress, point.x, point.y) + ctx.beginPath(); + ctx.arc(point.x, point.y, Math.max(1, size * 5), 0, 2 *Math.PI) + ctx.stroke(); + ctx.fill(); + ctx.closePath(); + + } + + } + + function getControlPoint(point) { + return Qt.point(point.x * .1, point.y * .1) + } + + } + + Item { + id: acquisitionItem + x: d.acquisitionPos.x - width / 2 + y: d.acquisitionPos.y - height / 2 + width: d.chartSize + height: d.chartSize + + Rectangle { + anchors.centerIn: parent + width: acquisitionChart.plotArea.width + height: acquisitionChart.plotArea.height + color: Style.backgroundColor + radius: width / 2 + } + + ColumnLayout { + anchors.centerIn: parent + width: acquisitionChart.plotArea.width * 0.8 + ColorIcon { + Layout.alignment: Qt.AlignHCenter + size: Style.bigIconSize + // color: Style.red + name: "/ui/images/power-grid.svg" + } + Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + text: d.formatValue(Math.abs(energyManager.currentPowerAcquisition)) + // color: energyManager.currentPowerAcquisition >= 0 ? Style.red : Style.yellow } } - if (energyManager.currentPowerAcquisition > 0) { - size = Math.abs(energyManager.currentPowerAcquisition) / biggest - drawDottedCurve(ctx, gridPos, consumptionPos, size, Style.red) + + ChartView { + id: acquisitionChart + anchors.fill: parent + legend.visible: false + margins { left: 0; top: 0; right: 0; bottom: 0 } + backgroundColor: "transparent" + animationOptions: root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation + + PieSeries { + size: 1 + holeSize: 0.8 + + PieSlice { + color: Style.red + borderColor: color + borderWidth: 0 + value: root.fromGrid + } + PieSlice { + color: Style.yellow + borderColor: color + borderWidth: 0 + value: root.toGrid + } + PieSlice { + color: Style.tooltipBackgroundColor + borderColor: color + borderWidth: 0 + value: energyManager.currentPowerAcquisition == 0 ? 1 : 0 + } + } + } + } + + + Item { + id: productionItem + x: d.productionPos.x - width / 2 + y: d.productionPos.y - height / 2 + width: d.chartSize + height: d.chartSize + visible: producers.count > 0 + + Rectangle { + anchors.centerIn: parent + width: productionChart.plotArea.width + height: productionChart.plotArea.height + color: Style.backgroundColor + radius: width / 2 } - ctx.restore(); - } - - function bezierCurvePoint(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, t) { - var x = Math.pow(1-t, 3)*p0x + 3*Math.pow(1-t, 2)*t*p1x + 3*(1-t)*Math.pow(t, 2)*p2x + Math.pow(t, 3)*p3x; - var y = Math.pow(1-t, 3)*p0y + 3*Math.pow(1-t, 2)*t*p1y + 3*(1-t)*Math.pow(t, 2)*p2y + Math.pow(t, 3)*p3y; - return Qt.point(x, y) - } - - function circlePoint(center, radius, angle) { - var x = center.x + radius * Math.cos(angle * 2 * Math.PI / 360) - var y = center.y + radius * Math.sin(angle * 2 * Math.PI / 360) - return Qt.point(x, y) - } - - function drawDottedCurve(ctx, start, end, size, color) { - var c1 = getControlPoint(start) - var c2 = getControlPoint(end) - ctx.fillStyle = color - ctx.strokeStyle = color - var count = 10; - for (var i = 1; i <= count; i++) { - var offset = 1 / count; - var progress = d.progress + i * offset - if (progress > 1) - progress -= 1 - var point = bezierCurvePoint(start.x, start.y, c1.x, c1.y, c2.x, c2.y, end.x, end.y, progress) -// print("painting", d.progress, point.x, point.y) - ctx.beginPath(); - ctx.arc(point.x, point.y, Math.max(1, size * 5), 0, 2 *Math.PI) - ctx.stroke(); - ctx.fill(); - ctx.closePath(); - + ColumnLayout { + anchors.centerIn: parent + width: productionChart.plotArea.width * 0.8 + ColorIcon { + Layout.alignment: Qt.AlignHCenter + size: Style.bigIconSize + // color: Style.yellow + name: "/ui/images/weathericons/weather-clear-day.svg" + } + Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + text: d.formatValue(Math.abs(energyManager.currentPowerProduction)) + // color: energyManager.currentPowerAcquisition >= 0 ? Style.red : Style.green + } } - } - function getControlPoint(point) { - return Qt.point(point.x * .1, point.y * .1) - } + ChartView { + id: productionChart + anchors.fill: parent + legend.visible: false + backgroundColor: "transparent" + margins { left: 0; top: 0; right: 0; bottom: 0 } + animationOptions: root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation - } + PieSeries { + size: 1 + holeSize: 0.8 - Item { - id: acquisitionItem - x: d.acquisitionPos.x - width / 2 - y: d.acquisitionPos.y - height / 2 - width: d.chartSize - height: d.chartSize - - Rectangle { - anchors.centerIn: parent - width: acquisitionChart.plotArea.width - height: acquisitionChart.plotArea.height - color: Style.backgroundColor - radius: width / 2 - } - - ColumnLayout { - anchors.centerIn: parent - width: acquisitionChart.plotArea.width * 0.8 - ColorIcon { - Layout.alignment: Qt.AlignHCenter - size: Style.bigIconSize - // color: Style.red - name: "/ui/images/power-grid.svg" + PieSlice { + color: Style.green + borderColor: color + borderWidth: 0 + value: root.fromProduction + } + PieSlice { + color: Style.purple + borderColor: color + borderWidth: 0 + value: root.toStorage + } + PieSlice { + color: Style.yellow + borderColor: color + borderWidth: 0 + value: root.toGrid + } + PieSlice { + color: Style.tooltipBackgroundColor + borderColor: color + borderWidth: 0 + value: energyManager.currentPowerProduction == 0 ? 1 : 0 + } + } } + } + + Item { + id: consumptionItem + x: d.consumptionPos.x - width / 2 + y: d.consumptionPos.y - height / 2 + width: d.chartSize + height: d.chartSize + + Rectangle { + anchors.centerIn: parent + width: consumptionChart.plotArea.width + height: consumptionChart.plotArea.height + color: Style.backgroundColor + radius: width / 2 + } + + ColumnLayout { + anchors.centerIn: parent + width: consumptionChart.plotArea.width * 0.8 + ColorIcon { + Layout.alignment: Qt.AlignHCenter + size: Style.bigIconSize + // color: Style.blue + name: "/ui/images/powersocket.svg" + } + Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + text: d.formatValue(energyManager.currentPowerConsumption) + // color: energyManager.currentPowerAcquisition >= 0 ? Style.red : Style.green + } + } + + ChartView { + id: consumptionChart + anchors.fill: parent + margins { left: 0; top: 0; right: 0; bottom: 0 } + legend.visible: false + backgroundColor: "transparent" + animationOptions: root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation + + PieSeries { + size: 1 + holeSize: 0.8 + + PieSlice { + color: Style.green + borderColor: color + borderWidth: 0 + value: root.fromProduction + } + PieSlice { + color: Style.red + borderColor: color + borderWidth: 0 + value: root.fromGrid + } + PieSlice { + color: Style.orange + borderColor: color + borderWidth: 0 + value: root.fromStorage + } + } + } + } + + + Item { + id: batteryItem + x: d.storagePos.x - width / 2 + y: d.storagePos.y - height / 2 + width: d.chartSize + height: d.chartSize + visible: batteries.count > 0 + + Rectangle { + anchors.centerIn: parent + width: batteryChart.plotArea.width + height: batteryChart.plotArea.height + color: Style.backgroundColor + radius: width / 2 + } + + ColumnLayout { + anchors.centerIn: parent + width: productionChart.plotArea.width * 0.8 + ColorIcon { + Layout.alignment: Qt.AlignHCenter + size: Style.bigIconSize + // color: Style.purple + name: "/ui/images/battery/battery-" + NymeaUtils.pad(Math.round(batteryChart.averageLevel / 10) * 10, 3) + ".svg" + } + Label { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + text: d.formatValue(Math.abs(energyManager.currentPowerStorage)) + // color: energyManager.currentPowerStorage >= 0 ? Style.green : Style.red + } + } + Label { - Layout.fillWidth: true + anchors.horizontalCenter: parent.horizontalCenter + y: batteryChart.y + batteryChart.plotArea.height * .2 horizontalAlignment: Text.AlignHCenter - text: d.formatValue(Math.abs(energyManager.currentPowerAcquisition)) -// color: energyManager.currentPowerAcquisition >= 0 ? Style.red : Style.yellow - } - } - - - ChartView { - id: acquisitionChart - anchors.fill: parent - legend.visible: false - margins { left: 0; top: 0; right: 0; bottom: 0 } - backgroundColor: "transparent" - animationOptions: root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation - - PieSeries { - size: 1 - holeSize: 0.8 - - PieSlice { - color: Style.red - borderColor: color - borderWidth: 0 - value: root.fromGrid - } - PieSlice { - color: Style.yellow - borderColor: color - borderWidth: 0 - value: root.toGrid - } - PieSlice { - color: Style.tooltipBackgroundColor - borderColor: color - borderWidth: 0 - value: energyManager.currentPowerAcquisition == 0 ? 1 : 0 - } - } - } - } - - - Item { - id: productionItem - x: d.productionPos.x - width / 2 - y: d.productionPos.y - height / 2 - width: d.chartSize - height: d.chartSize - visible: producers.count > 0 - - Rectangle { - anchors.centerIn: parent - width: productionChart.plotArea.width - height: productionChart.plotArea.height - color: Style.backgroundColor - radius: width / 2 - } - - ColumnLayout { - anchors.centerIn: parent - width: productionChart.plotArea.width * 0.8 - ColorIcon { - Layout.alignment: Qt.AlignHCenter - size: Style.bigIconSize - // color: Style.yellow - name: "/ui/images/weathericons/weather-clear-day.svg" - } - Label { - Layout.fillWidth: true - horizontalAlignment: Text.AlignHCenter - text: d.formatValue(Math.abs(energyManager.currentPowerProduction)) - // color: energyManager.currentPowerAcquisition >= 0 ? Style.red : Style.green - } - } - - - ChartView { - id: productionChart - anchors.fill: parent - legend.visible: false - backgroundColor: "transparent" - margins { left: 0; top: 0; right: 0; bottom: 0 } - animationOptions: root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation - - PieSeries { - size: 1 - holeSize: 0.8 - - PieSlice { - color: Style.green - borderColor: color - borderWidth: 0 - value: root.fromProduction - } - PieSlice { - color: Style.purple - borderColor: color - borderWidth: 0 - value: root.toStorage - } - PieSlice { - color: Style.yellow - borderColor: color - borderWidth: 0 - value: root.toGrid - } - PieSlice { - color: Style.tooltipBackgroundColor - borderColor: color - borderWidth: 0 - value: energyManager.currentPowerProduction == 0 ? 1 : 0 - } - } - } - } - - Item { - id: consumptionItem - x: d.consumptionPos.x - width / 2 - y: d.consumptionPos.y - height / 2 - width: d.chartSize - height: d.chartSize - - Rectangle { - anchors.centerIn: parent - width: consumptionChart.plotArea.width - height: consumptionChart.plotArea.height - color: Style.backgroundColor - radius: width / 2 - } - - ColumnLayout { - anchors.centerIn: parent - width: consumptionChart.plotArea.width * 0.8 - ColorIcon { - Layout.alignment: Qt.AlignHCenter - size: Style.bigIconSize - // color: Style.blue - name: "/ui/images/powersocket.svg" - } - Label { - Layout.fillWidth: true - horizontalAlignment: Text.AlignHCenter - text: d.formatValue(energyManager.currentPowerConsumption) - // color: energyManager.currentPowerAcquisition >= 0 ? Style.red : Style.green - } - } - - ChartView { - id: consumptionChart - anchors.fill: parent - margins { left: 0; top: 0; right: 0; bottom: 0 } - legend.visible: false - backgroundColor: "transparent" - animationOptions: root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation - - PieSeries { - size: 1 - holeSize: 0.8 - - PieSlice { - color: Style.green - borderColor: color - borderWidth: 0 - value: root.fromProduction - } - PieSlice { - color: Style.red - borderColor: color - borderWidth: 0 - value: root.fromGrid - } - PieSlice { - color: Style.orange - borderColor: color - borderWidth: 0 - value: root.fromStorage - } - } - } - } - - - Item { - id: batteryItem - x: d.storagePos.x - width / 2 - y: d.storagePos.y - height / 2 - width: d.chartSize - height: d.chartSize - visible: batteries.count > 0 - - Rectangle { - anchors.centerIn: parent - width: batteryChart.plotArea.width - height: batteryChart.plotArea.height - color: Style.backgroundColor - radius: width / 2 - } - - ColumnLayout { - anchors.centerIn: parent - width: productionChart.plotArea.width * 0.8 - ColorIcon { - Layout.alignment: Qt.AlignHCenter - size: Style.bigIconSize - // color: Style.purple - name: "/ui/images/battery/battery-" + NymeaUtils.pad(Math.round(batteryChart.averageLevel / 10) * 10, 3) + ".svg" - } - Label { - Layout.fillWidth: true - horizontalAlignment: Text.AlignHCenter - text: d.formatValue(Math.abs(energyManager.currentPowerStorage)) + font: Style.smallFont + text: batteryChart.averageLevel + "%" // color: energyManager.currentPowerStorage >= 0 ? Style.green : Style.red } - } - Label { - anchors.horizontalCenter: parent.horizontalCenter - y: batteryChart.y + batteryChart.plotArea.height * .2 - horizontalAlignment: Text.AlignHCenter - font: Style.smallFont - text: batteryChart.averageLevel + "%" -// color: energyManager.currentPowerStorage >= 0 ? Style.green : Style.red - } + ChartView { + id: batteryChart + anchors.fill: parent + margins { left: 0; top: 0; right: 0; bottom: 0 } + legend.visible: false + backgroundColor: "transparent" + animationOptions: root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation - ChartView { - id: batteryChart - anchors.fill: parent - margins { left: 0; top: 0; right: 0; bottom: 0 } - legend.visible: false - backgroundColor: "transparent" - animationOptions: root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation - - property double totalCapacity: { - var totalCapacity = 0; - for (var i = 0; i < batteriesRepeater.count; i++) { - totalCapacity += batteriesRepeater.itemAt(i).capacityState.value + property double totalCapacity: { + var totalCapacity = 0; + for (var i = 0; i < batteriesRepeater.count; i++) { + totalCapacity += batteriesRepeater.itemAt(i).capacityState.value + } + return totalCapacity; } - return totalCapacity; - } - property double averageLevel: { - if (batteriesRepeater.count == 0) { - return 0; + property double averageLevel: { + if (batteriesRepeater.count == 0) { + return 0; + } + + var averageLevel = 0; + for (var i = 0; i < batteriesRepeater.count; i++) { + averageLevel += batteriesRepeater.itemAt(i).batteryLevelState.value + } + averageLevel /= batteriesRepeater.count + return averageLevel; } - var averageLevel = 0; - for (var i = 0; i < batteriesRepeater.count; i++) { - averageLevel += batteriesRepeater.itemAt(i).batteryLevelState.value + Repeater { + id: batteriesRepeater + model: batteries + delegate: Item { + property Thing thing: batteries.get(index) + property State batteryLevelState: thing.stateByName("batteryLevel") + property State capacityState: thing.stateByName("capacity") + } } - averageLevel /= batteriesRepeater.count - return averageLevel; - } - Repeater { - id: batteriesRepeater - model: batteries - delegate: Item { - property Thing thing: batteries.get(index) - property State batteryLevelState: thing.stateByName("batteryLevel") - property State capacityState: thing.stateByName("capacity") - } - } + PieSeries { + id: batterySeries + size: 1 + holeSize: 0.8 - PieSeries { - id: batterySeries - size: 1 - holeSize: 0.8 - - PieSlice { - color: energyManager.currentPowerStorage == 0 - ? Style.foregroundColor - : root.toStorage > 0 - ? Style.purple - : Style.orange - borderColor: color - borderWidth: 0 - value: batteryChart.averageLevel - } - PieSlice { - color: Style.tooltipBackgroundColor - borderColor: color - borderWidth: 0 - value: 100 - batteryChart.averageLevel + PieSlice { + color: energyManager.currentPowerStorage == 0 + ? Style.foregroundColor + : root.toStorage > 0 + ? Style.purple + : Style.orange + borderColor: color + borderWidth: 0 + value: batteryChart.averageLevel + } + PieSlice { + color: Style.tooltipBackgroundColor + borderColor: color + borderWidth: 0 + value: 100 - batteryChart.averageLevel + } } } } } - } - -//ChartView { -// id: consumptionPieChart -// backgroundColor: "transparent" -// animationOptions: animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation -// title: qsTr("My energy mix") -// titleColor: Style.foregroundColor -// legend.visible: false - -// margins.left: 0 -// margins.right: 0 -// margins.bottom: 0 -// margins.top: 0 - -// property bool animationsEnabled: true -// property EnergyManager energyManager: null - -// ThingsProxy { -// id: batteries -// engine: _engine -// shownInterfaces: ["energystorage"] -// } - -// PieSeries { -// id: consumptionBalanceSeries -// size: 0.88 -// holeSize: 0.7 - -// property double fromGrid: Math.max(0, energyManager.currentPowerAcquisition) -// property double fromStorage: -Math.min(0, energyManager.currentPowerStorage) -// property double toStorage: -Math.min(0, -energyManager.currentPowerStorage) -// property double fromProduction: energyManager.currentPowerConsumption - fromGrid - fromStorage -// property double toGrid: Math.max(0, - energyManager.currentPowerAcquisition) - -// PieSlice { -// color: Style.red -// borderColor: color -// borderWidth: 0 -// value: consumptionBalanceSeries.fromGrid -// } -// PieSlice { -// color: Style.green -// borderColor: color -// borderWidth: 0 -// value: consumptionBalanceSeries.fromProduction -// } -// PieSlice { -// color: Style.purple -// borderColor: color -// borderWidth: 0 -// value: consumptionBalanceSeries.fromStorage -// } -// PieSlice { -// color: Style.yellow -// borderColor: color -// borderWidth: 0 -// value: consumptionBalanceSeries.toGrid -// } -// PieSlice { -// color: Style.orange -// borderColor: color -// borderWidth: 0 -// value: consumptionBalanceSeries.toStorage -// } - -// PieSlice { -// color: Style.tooltipBackgroundColor -// borderColor: color -// borderWidth: 0 -// value: consumptionBalanceSeries.fromGrid == 0 && consumptionBalanceSeries.fromProduction == 0 && consumptionBalanceSeries.fromStorage == 0 ? 1 : 0 -// } -// } - -// Item { -// id: centerItem - -// x: consumptionPieChart.plotArea.x + (consumptionPieChart.plotArea.width - width) / 2 -// y: consumptionPieChart.plotArea.y + (consumptionPieChart.plotArea.height - height) / 2 -// width: consumptionPieChart.plotArea.width * 0.65 -// height: width - -//// Rectangle { -//// anchors.fill: parent -//// color: "white" -//// } - -// QtObject { -// id: d -// property double progress: 0 -// onProgressChanged: canvas.requestPaint() -// } - -// NumberAnimation { -// id: progressAnimation -// target: d -// property: "progress" -// from: 0 -// to: 1 -// running: true -// loops: Animation.Infinite -// duration: 5000 -// } - - -// Canvas { -// id: canvas -// anchors.fill: parent - -// property int itemCount: batteries.count > 0 ? 4 : 3 - -// ColorIcon { -// property var point: canvas.circlePoint(Qt.point(canvas.width / 2, canvas.height / 2), canvas.height / 2, -90) -// x: point.x - width / 2 -// y: point.y - height / 2 -// name: "weathericons/weather-clear-day" -// } -// ColorIcon { -// property var point: canvas.circlePoint(Qt.point(canvas.width / 2, canvas.height / 2), canvas.height / 2, -90 + 360 / canvas.itemCount) -// x: point.x - width / 2 -// y: point.y - height / 2 -// name: "battery/battery-080" -// visible: batteries.count > 0 -// } -// ColorIcon { -// property var point: canvas.circlePoint(Qt.point(canvas.width / 2, canvas.height / 2), canvas.height / 2, -90 + 360 / canvas.itemCount * (batteries.count > 0 ? 2 : 1)) -// x: point.x - width / 2 -// y: point.y - height / 2 -// name: "things" -// } -// ColorIcon { -// property var point: canvas.circlePoint(Qt.point(canvas.width / 2, canvas.height / 2), canvas.height / 2, -90 + 360 / canvas.itemCount * (batteries.count > 0 ? 3 : 2)) -// x: point.x - width / 2 -// y: point.y - height / 2 -// name: "energy" -// } - -// onPaint: { -// var ctx = getContext("2d"); - -// var solarPos = circlePoint(Qt.point(0, 0), height / 2, -90) -// var storagePos = circlePoint(Qt.point(0, 0), height / 2, -90 / 360 * itemCount * 1) -// var consumptionPos = circlePoint(Qt.point(0, 0), height / 2, -90 + 360 / itemCount * (batteries.count > 0 ? 2 : 1)) -// var gridPos = circlePoint(Qt.point(0, 0), height / 2, -90 + 360 / itemCount * (batteries.count > 0 ? 3 : 2)) - -// ctx.save(); -// ctx.reset() - -// ctx.translate(width / 2, height / 2); - -// ctx.strokeStyle = Style.foregroundColor -// ctx.fillStyle = Style.foregroundColor -// ctx.lineWidth = 2 - -//// ctx.beginPath(); -//// ctx.moveTo(0, -height / 2); -//// ctx.bezierCurveTo(0, -height / 10, -width / 10, 0, -width / 2, 0) -//// ctx.stroke(); -//// ctx.closePath(); - -//// ctx.beginPath(); -//// ctx.moveTo(-width / 2, 0); -//// ctx.bezierCurveTo(-width / 10, 0, 0, height / 10, 0, height / 2) -//// ctx.stroke(); -//// ctx.closePath(); - -//// ctx.beginPath(); -//// ctx.moveTo(0, height / 2); -//// ctx.bezierCurveTo(0, height / 10, width / 10, 0, width / 2, 0) -//// ctx.stroke(); -//// ctx.closePath(); - -//// ctx.beginPath(); -//// ctx.moveTo(width / 2, 0); -//// ctx.bezierCurveTo(width / 10, 0, 0, -height / 10, 0, -height / 2) -//// ctx.stroke(); -//// ctx.closePath(); - -// var size = Math.abs(energyManager.currentPowerAcquisition) / Math.abs(energyManager.currentPowerProduction) -// drawDottedCurve(ctx, solarPos, gridPos, size) - -// size = Math.abs(energyManager.currentPowerConsumption) / Math.abs(energyManager.currentPowerProduction) -// drawDottedCurve(ctx, solarPos, consumptionPos, size) - -// if (batteries.count > 0) { -// size = Math.abs(energyManager.currentPowerStorage) / Math.abs(energyManager.currentPowerProduction) -// drawDottedCurve(ctx, solarPos, storagePos, size) - -// if (energyManager.currentPowerStorage < 0) { -// size = Math.abs(energyManager.currentPowerStorage) / Math.abs(energyManager.currentPowerConsumption) -// drawDottedCurve(ctx, storagePos, consumptionPos, size) -// } -// } - -// if (energyManager.currentPowerAcquisition > 0) { -// size = Math.abs(energyManager.currentPowerAcquisition) / Math.abs(energyManager.currentPowerConsumption) -// drawDottedCurve(ctx, gridPos, consumptionPos, size) -// } - -//// var count = 5; -//// for (var i = 1; i <= count; i++) { -//// var offset = 1 / count; -//// var progress = d.progress + i * offset -//// if (progress > 1) -//// progress -= 1 -//// var point = bezierCurvePoint(width / 2, 0, width / 10, 0, 0, -height / 10, 0, -height / 2, progress) -//// // print("painting", d.progress, point.x, point.y) -//// ctx.beginPath(); -//// ctx.arc(point.x, point.y, 4, 0, 2 *Math.PI) -//// ctx.stroke(); -//// ctx.closePath(); - -//// } - - -// ctx.restore(); -// } - -// function bezierCurvePoint(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, t) { -// var x = Math.pow(1-t, 3)*p0x + 3*Math.pow(1-t, 2)*t*p1x + 3*(1-t)*Math.pow(t, 2)*p2x + Math.pow(t, 3)*p3x; -// var y = Math.pow(1-t, 3)*p0y + 3*Math.pow(1-t, 2)*t*p1y + 3*(1-t)*Math.pow(t, 2)*p2y + Math.pow(t, 3)*p3y; -// return Qt.point(x, y) -// } - -// function circlePoint(center, radius, angle) { -// var x = center.x + radius * Math.cos(angle * 2 * Math.PI / 360) -// var y = center.y + radius * Math.sin(angle * 2 * Math.PI / 360) -// return Qt.point(x, y) -// } - -// function drawDottedCurve(ctx, start, end, size) { -// var c1 = getControlPoint(start) -// var c2 = getControlPoint(end) -// var count = 10; -// for (var i = 1; i <= count; i++) { -// var offset = 1 / count; -// var progress = d.progress + i * offset -// if (progress > 1) -// progress -= 1 -// var point = bezierCurvePoint(start.x, start.y, c1.x, c1.y, c2.x, c2.y, end.x, end.y, progress) -// // print("painting", d.progress, point.x, point.y) -// ctx.beginPath(); -// ctx.arc(point.x, point.y, size * 5, 0, 2 *Math.PI) -// ctx.stroke(); -// ctx.fill(); -// ctx.closePath(); - -// } - -// } - -// function getControlPoint(point) { -// return Qt.point(point.x * .1, point.y * .1) -// } - -// } - -// } - - -// Column { -// id: centerLayout -// x: consumptionPieChart.plotArea.x + (consumptionPieChart.plotArea.width - width) / 2 -// y: consumptionPieChart.plotArea.y + (consumptionPieChart.plotArea.height - height) / 2 -// width: consumptionPieChart.plotArea.width * 0.65 -//// height: consumptionPieChart.plotArea.height * 0.65 -// height: childrenRect.height -// spacing: Style.smallMargins - -// visible: false - -// ColumnLayout { -// width: parent.width -// spacing: 0 -// Label { -// text: qsTr("Consumption") -// font: Style.smallFont -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -// } - -// Label { -// text: "%1 %2" -// .arg((energyManager.currentPowerConsumption / (energyManager.currentPowerConsumption > 1000 ? 1000 : 1)).toFixed(1)) -// .arg(energyManager.currentPowerConsumption > 1000 ? "kW" : "W") -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -//// font: Style.smallFont -// color: Style.blue -// } -// } -// ColumnLayout { -// width: parent.width -// spacing: 0 -// Label { -// text: qsTr("Production") -// font: Style.smallFont -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -// } - -// Label { -// property double absValue: Math.abs(energyManager.currentPowerProduction) -// text: "%1 %2" -// .arg((absValue / (absValue > 1000 ? 1000 : 1)).toFixed(1)) -// .arg(absValue > 1000 ? "kW" : "W") -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -//// font: Style.bigFont -// color: Style.yellow - -// } -// } - - -// ColumnLayout { -// width: parent.width -// spacing: 0 -// Label { -// text: qsTr("From grid") -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -// font: Style.extraSmallFont -// } -// Label { -// property double absValue: consumptionBalanceSeries.fromGrid -// color: Style.red -// text: "%1 %2" -// .arg((absValue / (absValue > 1000 ? 1000 : 1)).toFixed(1)) -// .arg(absValue > 1000 ? "kW" : "W") -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -// font: Style.smallFont -// } -// } - - -// ColumnLayout { -// width: parent.width -// spacing: 0 -// Label { -// text: qsTr("From self production") -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -// font: Style.extraSmallFont -// } -// Label { -// color: Style.green -// property double absValue: consumptionBalanceSeries.fromProduction -// text: "%1 %2".arg((absValue / (absValue > 1000 ? 1000 : 1)).toFixed(1)) -// .arg(absValue > 1000 ? "kW" : "W") -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -// font: Style.smallFont -// } -// } -// ColumnLayout { -// width: parent.width -// spacing: 0 -// visible: batteries.count > 0 -// Label { -// text: energyManager.currentPowerStorage < 0 ? qsTr("From battery") : qsTr("To battery") -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -// font: Style.extraSmallFont -// } -// Label { -// color: value < 0 ? Style.purple : Style.orange -// property double value: energyManager.currentPowerStorage -// property double absValue: Math.abs(energyManager.currentPowerStorage) -// text: "%1 %2".arg((absValue / (absValue > 1000 ? 1000 : 1)).toFixed(1)) -// .arg(absValue > 1000 ? "kW" : "W") -// Layout.fillWidth: true -// horizontalAlignment: Text.AlignHCenter -// font: Style.smallFont -// } -// } -// } -//} diff --git a/nymea-app/ui/mainviews/energy/PowerBalanceHistory.qml b/nymea-app/ui/mainviews/energy/PowerBalanceHistory.qml index 4c8ae4d7..9d0ef50b 100644 --- a/nymea-app/ui/mainviews/energy/PowerBalanceHistory.qml +++ b/nymea-app/ui/mainviews/energy/PowerBalanceHistory.qml @@ -8,6 +8,8 @@ import "qrc:/ui/components" Item { id: root + property bool titleVisible: true + PowerBalanceLogs { id: powerBalanceLogs engine: _engine @@ -26,6 +28,8 @@ Item { id: d property date now: new Date() + property var selectedSeries: null + readonly property int range: selectionTabs.currentValue.range readonly property int sampleRate: selectionTabs.currentValue.sampleRate readonly property int visibleValues: range / sampleRate @@ -63,6 +67,13 @@ Item { return timestamp } + function selectSeries(series) { + if (d.selectedSeries == series) { + d.selectedSeries = null + } else { + d.selectedSeries = series + } + } } Connections { @@ -105,12 +116,20 @@ Item { anchors.fill: parent spacing: 0 -// Label { -// Layout.fillWidth: true -// Layout.margins: Style.smallMargins -// horizontalAlignment: Text.AlignHCenter -// text: qsTr("My production history") -// } + Label { + id: titleLabel + Layout.fillWidth: true + Layout.margins: Style.smallMargins + horizontalAlignment: Text.AlignHCenter + text: qsTr("My power balance history") + visible: root.titleVisible + MouseArea { + anchors.fill: parent + onClicked: { + pageStack.push(Qt.resolvedUrl("PowerBalanceHistoryPage.qml")) + } + } + } SelectionTabs { id: selectionTabs @@ -265,6 +284,7 @@ Item { shadesVisible: false labelsColor: Style.foregroundColor } + AreaSeries { id: selfProductionConsumptionSeries axisX: dateTimeAxis @@ -273,8 +293,10 @@ Item { // borderWidth: 2 borderColor: color name: qsTr("From self production") + opacity: d.selectedSeries == null || d.selectedSeries == selfProductionConsumptionSeries ? 1 : 0.3 // visible: false + onClicked: d.selectedSeries(selfProductionConsumptionSeries) lowerSeries: LineSeries { id: zeroSeries XYPoint { x: dateTimeAxis.min.getTime(); y: 0 } @@ -331,9 +353,11 @@ Item { color: Style.purple borderWidth: 0 borderColor: color + opacity: d.selectedSeries == null || d.selectedSeries == toStorageSeries ? 1 : 0.3 visible: root.batteries.count > 0 name: qsTr("To battery") + onClicked: d.selectSeries(toStorageSeries) function calculateValue(entry) { return selfProductionConsumptionSeries.calculateValue(entry) + Math.max(0, entry.storage); @@ -352,7 +376,6 @@ Item { } } - AreaSeries { id: returnSeries axisX: dateTimeAxis @@ -361,8 +384,11 @@ Item { borderWidth: 0 borderColor: color name: qsTr("To grid") + opacity: d.selectedSeries == null || d.selectedSeries == returnSeries ? 1 : 0.3 // visible: false + onClicked: d.selectSeries(returnSeries) + function calculateValue(entry) { return toStorageSeries.calculateValue(entry) + Math.max(0, -entry.acquisition) } @@ -387,8 +413,10 @@ Item { borderWidth: 0 borderColor: color name: qsTr("From battery") + opacity: d.selectedSeries == null || d.selectedSeries == fromStorageSeries ? 1 : 0.3 visible: root.batteries.count > 0 + onClicked: d.selectSeries(fromStorageSeries) lowerSeries: selfProductionConsumptionUpperSeries upperSeries: LineSeries { id: fromStorageUpperSeries @@ -406,7 +434,6 @@ Item { } } - AreaSeries { id: acquisitionSeries axisX: dateTimeAxis @@ -415,8 +442,11 @@ Item { borderWidth: 0 borderColor: color name: qsTr("From grid") + opacity: d.selectedSeries == null || d.selectedSeries == acquisitionSeries ? 1 : 0.3 // visible: false + onClicked: d.selectSeries(acquisitionSeries) + lowerSeries: fromStorageUpperSeries upperSeries: LineSeries { id: acquisitionUpperSeries @@ -476,89 +506,158 @@ Item { } RowLayout { + id: legend anchors { left: parent.left; bottom: parent.bottom; right: parent.right } anchors.leftMargin: chartView.plotArea.x height: Style.smallIconSize anchors.margins: Style.margins - Item { + MouseArea { Layout.fillWidth: true Layout.fillHeight: true - ColorIcon { - name: "weathericons/weather-clear-day" - size: Style.smallIconSize - color: Style.green + onClicked: d.selectSeries(selfProductionConsumptionSeries) + opacity: selfProductionConsumptionSeries.opacity + Row { anchors.centerIn: parent + spacing: Style.smallMargins + ColorIcon { + name: "weathericons/weather-clear-day" + size: Style.smallIconSize + color: Style.green + } + Label { + width: parent.parent.width - x + elide: Text.ElideRight + visible: legend.width > 500 + text: qsTr("Produced") + anchors.verticalCenter: parent.verticalCenter + font: Style.smallFont + } } } - Item { + MouseArea { Layout.fillWidth: true Layout.fillHeight: true + onClicked: d.selectSeries(acquisitionSeries) + opacity: acquisitionSeries.opacity Row { anchors.centerIn: parent - ColorIcon { - name: "power-grid" - size: Style.smallIconSize - color: Style.red + spacing: Style.smallMargins + Row { + ColorIcon { + name: "power-grid" + size: Style.smallIconSize + color: Style.red + } + ColorIcon { + name: "arrow-down" + size: Style.smallIconSize + color: Style.red + } } - ColorIcon { - name: "arrow-down" - size: Style.smallIconSize - color: Style.red + Label { + width: parent.parent.width - x + elide: Text.ElideRight + visible: legend.width > 500 + text: qsTr("From grid") + anchors.verticalCenter: parent.verticalCenter + font: Style.smallFont } } } - Item { + + MouseArea { Layout.fillWidth: true Layout.fillHeight: true + onClicked: d.selectSeries(returnSeries) + opacity: returnSeries.opacity Row { anchors.centerIn: parent - ColorIcon { - name: "power-grid" - size: Style.smallIconSize - color: Style.yellow + spacing: Style.smallMargins + Row { + ColorIcon { + name: "power-grid" + size: Style.smallIconSize + color: Style.yellow + } + ColorIcon { + name: "arrow-up" + size: Style.smallIconSize + color: Style.yellow + } } - ColorIcon { - name: "arrow-up" - size: Style.smallIconSize - color: Style.yellow + Label { + width: parent.parent.width - x + elide: Text.ElideRight + visible: legend.width > 500 + text: qsTr("To grid") + anchors.verticalCenter: parent.verticalCenter + font: Style.smallFont } } } - Item { + + MouseArea { Layout.fillWidth: true Layout.fillHeight: true visible: batteries.count > 0 + onClicked: d.selectSeries(toStorageSeries) + opacity: toStorageSeries.opacity Row { anchors.centerIn: parent - ColorIcon { - name: "battery/battery-080" - size: Style.smallIconSize - color: Style.purple + spacing: Style.smallMargins + Row { + ColorIcon { + name: "battery/battery-080" + size: Style.smallIconSize + color: Style.purple + } + ColorIcon { + name: "plus" + size: Style.smallIconSize + color: Style.purple + } } - ColorIcon { - name: "plus" - size: Style.smallIconSize - color: Style.purple + Label { + width: parent.parent.width - x + elide: Text.ElideRight + visible: legend.width > 500 + text: qsTr("To battery") + anchors.verticalCenter: parent.verticalCenter + font: Style.smallFont } } } - Item { + + MouseArea { Layout.fillWidth: true Layout.fillHeight: true visible: batteries.count > 0 + onClicked: d.selectSeries(fromStorageSeries) + opacity: fromStorageSeries.opacity Row { anchors.centerIn: parent - ColorIcon { - name: "battery/battery-040" - size: Style.smallIconSize - color: Style.orange + spacing: Style.smallMargins + Row { + ColorIcon { + name: "battery/battery-040" + size: Style.smallIconSize + color: Style.orange + } + ColorIcon { + name: "minus" + size: Style.smallIconSize + color: Style.orange + } } - ColorIcon { - name: "minus" - size: Style.smallIconSize - color: Style.orange + Label { + width: parent.parent.width - x + elide: Text.ElideRight + visible: legend.width > 500 + text: qsTr("From battery") + anchors.verticalCenter: parent.verticalCenter + font: Style.smallFont } } } @@ -574,6 +673,7 @@ Item { hoverEnabled: true preventStealing: tooltipping || dragging + propagateComposedEvents: true property int startMouseX: 0 property bool dragging: false diff --git a/nymea-app/ui/mainviews/energy/PowerBalanceHistoryPage.qml b/nymea-app/ui/mainviews/energy/PowerBalanceHistoryPage.qml new file mode 100644 index 00000000..646bf656 --- /dev/null +++ b/nymea-app/ui/mainviews/energy/PowerBalanceHistoryPage.qml @@ -0,0 +1,21 @@ +import QtQuick 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import Nymea 1.0 +import "qrc:/ui/components" + +Page { + id: root + + header: NymeaHeader { + text: qsTr("My power balance history") + backButtonVisible: true + onBackPressed: pageStack.pop() + } + + PowerBalanceHistory { + id: powerBalanceHistory + anchors.fill: parent + titleVisible: false + } +} diff --git a/nymea-app/ui/mainviews/energy/PowerBalanceStats.qml b/nymea-app/ui/mainviews/energy/PowerBalanceStats.qml index 55d36029..946c2e1e 100644 --- a/nymea-app/ui/mainviews/energy/PowerBalanceStats.qml +++ b/nymea-app/ui/mainviews/energy/PowerBalanceStats.qml @@ -10,6 +10,8 @@ StatsBase { property EnergyManager energyManager: null + property bool titleVisible: true + property ThingsProxy producers: ThingsProxy { engine: _engine shownInterfaces: ["smartmeterproducer"] @@ -22,6 +24,8 @@ StatsBase { property var config: root.configs[selectionTabs.currentValue.config] property int startOffset: 0 + property var selectedSet: null + property date startTime: root.calculateTimestamp(config.startTime(), config.sampleRate, startOffset) property date endTime: root.calculateTimestamp(config.startTime(), config.sampleRate, startOffset + config.count) @@ -38,6 +42,14 @@ StatsBase { refresh() } + function selectSet(set) { + if (d.selectedSet === set) { + d.selectedSet = null + } else { + d.selectedSet = set + } + } + function refresh() { if (powerBalanceLogs.loadingInhibited) { return; @@ -106,12 +118,17 @@ StatsBase { anchors.fill: parent spacing: 0 -// Label { -// Layout.fillWidth: true -// Layout.margins: Style.smallMargins -// horizontalAlignment: Text.AlignHCenter -// text: qsTr("Totals") -// } + Label { + Layout.fillWidth: true + Layout.margins: Style.smallMargins + horizontalAlignment: Text.AlignHCenter + text: qsTr("Totals") + visible: root.titleVisible + MouseArea { + anchors.fill: parent + onClicked: pageStack.push(Qt.resolvedUrl("PowerBalanceStatsPage.qml"), {energyManager: root.energyManager, producers: root.producers}) + } + } SelectionTabs { id: selectionTabs @@ -278,7 +295,7 @@ StatsBase { BarSet { id: consumptionSet label: qsTr("Consumed") - color: Style.blue + color: Qt.rgba(Style.blue.r, Style.blue.g, Style.blue.b, d.selectedSet == null || d.selectedSet == consumptionSet ? 1 : 0.3) borderColor: color borderWidth: 0 values: { @@ -292,7 +309,7 @@ StatsBase { BarSet { id: productionSet label: qsTr("Produced") - color: Style.green + color: Qt.rgba(Style.green.r, Style.green.g, Style.green.b, d.selectedSet == null || d.selectedSet == productionSet ? 1 : 0.3) borderColor: color borderWidth: 0 values: { @@ -306,7 +323,7 @@ StatsBase { BarSet { id: acquisitionSet label: qsTr("From grid") - color: Style.red + color: Qt.rgba(Style.red.r, Style.red.g, Style.red.b, d.selectedSet == null || d.selectedSet == acquisitionSet ? 1 : 0.3) borderColor: color borderWidth: 0 values: { @@ -320,7 +337,7 @@ StatsBase { BarSet { id: returnSet label: qsTr("To grid") - color: Style.yellow + color: Qt.rgba(Style.yellow.r, Style.yellow.g, Style.yellow.b, d.selectedSet == null || d.selectedSet == returnSet ? 1 : 0.3) borderColor: color borderWidth: 0 values: { @@ -335,64 +352,113 @@ StatsBase { } RowLayout { + id: legend anchors { left: parent.left; bottom: parent.bottom; right: parent.right } anchors.leftMargin: chartView.plotArea.x height: Style.smallIconSize anchors.margins: Style.margins - Item { + MouseArea { Layout.fillWidth: true Layout.fillHeight: true - ColorIcon { - name: "powersocket" - size: Style.smallIconSize - color: Style.blue + onClicked: d.selectSet(consumptionSet) + Row { anchors.centerIn: parent + spacing: Style.smallMargins + ColorIcon { + name: "powersocket" + size: Style.smallIconSize + color: Style.blue + } + Label { + width: parent.parent.width - x + elide: Text.ElideRight + visible: legend.width > 500 + text: qsTr("Consumed") + anchors.verticalCenter: parent.verticalCenter + font: Style.smallFont + } } } - Item { + MouseArea { Layout.fillWidth: true Layout.fillHeight: true - ColorIcon { - name: "weathericons/weather-clear-day" - size: Style.smallIconSize - color: Style.green + onClicked: d.selectSet(productionSet) + Row { anchors.centerIn: parent + spacing: Style.smallMargins + ColorIcon { + name: "weathericons/weather-clear-day" + size: Style.smallIconSize + color: Style.green + } + Label { + width: parent.parent.width - x + elide: Text.ElideRight + visible: legend.width > 500 + text: qsTr("Produced") + anchors.verticalCenter: parent.verticalCenter + font: Style.smallFont + } } } - Item { + MouseArea { Layout.fillWidth: true Layout.fillHeight: true + onClicked: d.selectSet(acquisitionSet) Row { anchors.centerIn: parent - ColorIcon { - name: "power-grid" - size: Style.smallIconSize - color: Style.red + spacing: Style.smallMargins + Row { + ColorIcon { + name: "power-grid" + size: Style.smallIconSize + color: Style.red + } + ColorIcon { + name: "arrow-down" + size: Style.smallIconSize + color: Style.red + } } - ColorIcon { - name: "arrow-down" - size: Style.smallIconSize - color: Style.red + Label { + width: parent.parent.width - x + elide: Text.ElideRight + visible: legend.width > 500 + text: qsTr("From grid") + anchors.verticalCenter: parent.verticalCenter + font: Style.smallFont } } } - Item { + MouseArea { Layout.fillWidth: true Layout.fillHeight: true + onClicked: d.selectSet(returnSet) Row { anchors.centerIn: parent - ColorIcon { - name: "power-grid" - size: Style.smallIconSize - color: Style.yellow + spacing: Style.smallMargins + Row { + ColorIcon { + name: "power-grid" + size: Style.smallIconSize + color: Style.yellow + } + ColorIcon { + name: "arrow-up" + size: Style.smallIconSize + color: Style.yellow + } } - ColorIcon { - name: "arrow-up" - size: Style.smallIconSize - color: Style.yellow + Label { + width: parent.parent.width - x + elide: Text.ElideRight + visible: legend.width > 500 + text: qsTr("To grid") + anchors.verticalCenter: parent.verticalCenter + font: Style.smallFont } } } @@ -564,7 +630,7 @@ StatsBase { Rectangle { width: Style.extraSmallFont.pixelSize height: width - color: Style.yellow + color: Style.green } Label { text: d.startOffset !== undefined ? qsTr("Produced: %1 kWh").arg(productionSet.at(toolTip.idx).toFixed(2)) : "" @@ -586,7 +652,7 @@ StatsBase { Rectangle { width: Style.extraSmallFont.pixelSize height: width - color: Style.green + color: Style.yellow } Label { text: d.startOffset !== undefined ? qsTr("To grid: %1 kWh").arg(returnSet.at(toolTip.idx).toFixed(2)) : "" diff --git a/nymea-app/ui/mainviews/energy/PowerBalanceStatsPage.qml b/nymea-app/ui/mainviews/energy/PowerBalanceStatsPage.qml new file mode 100644 index 00000000..683beb43 --- /dev/null +++ b/nymea-app/ui/mainviews/energy/PowerBalanceStatsPage.qml @@ -0,0 +1,24 @@ +import QtQuick 2.3 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import Nymea 1.0 +import "qrc:/ui/components" + +Page { + id: root + + property alias energyManager: powerBalanceStats + property alias producers: powerBalanceStats.producers + + header: NymeaHeader { + text: qsTr("Power balance totals") + backButtonVisible: true + onBackPressed: pageStack.pop() + } + + PowerBalanceStats { + id: powerBalanceStats + anchors.fill: parent + titleVisible: false + } +} diff --git a/nymea-app/ui/utils/NymeaUtils.qml b/nymea-app/ui/utils/NymeaUtils.qml index 09a20d8a..84a147c0 100644 --- a/nymea-app/ui/utils/NymeaUtils.qml +++ b/nymea-app/ui/utils/NymeaUtils.qml @@ -154,7 +154,7 @@ Item { property bool inhibitChartsAnimation: PlatformHelper.deviceModel.startsWith("SM-G950") // Samsung S8 has a buggy GPU driver :( property int chartsAnimationOptions: !inhibitChartsAnimation ? ChartView.SeriesAnimations : ChartView.NoAnimation - function generateColor(baseColor, index) { + function generateColor(baseColor, index, alpha) { var stepSize = 30 var baseHSV = rgb2hsv(baseColor.r, baseColor.g, baseColor.b) var currentHue = baseHSV[0] @@ -170,7 +170,7 @@ Item { } handledColors.push(currentHue) } - return Qt.hsva(currentHue / 360, baseHSV[1], baseHSV[2], 1); + return Qt.hsva(currentHue / 360, baseHSV[1], baseHSV[2], alpha || 1); } function rgb2hsv(r,g,b) {