More improvements on the energy view

Bring back title headers
allow opening a single chart fullscreen
allow selecting individual series in a chart
This commit is contained in:
Michael Zanetti 2022-11-26 01:27:52 +01:00
parent 80f74e192f
commit 32853ce612
17 changed files with 1064 additions and 1012 deletions

View File

@ -281,5 +281,11 @@
<file>ui/mainviews/energy/PowerBalanceHistory.qml</file>
<file>ui/mainviews/energy/CurrentPowerBalancePieChart.qml</file>
<file>ui/components/MultiSelectionTabs.qml</file>
<file>ui/mainviews/energy/PowerBalanceHistoryPage.qml</file>
<file>ui/mainviews/energy/CurrentPowerBalancePage.qml</file>
<file>ui/mainviews/energy/PowerBalanceStatsPage.qml</file>
<file>ui/mainviews/energy/ConsumersHistoryPage.qml</file>
<file>ui/mainviews/energy/ConsumerStatsPage.qml</file>
<file>ui/mainviews/energy/ConsumersPieChartPage.qml</file>
</qresource>
</RCC>

View File

@ -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: [

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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
}
}

View File

@ -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)) : ""

View File

@ -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
}
}

View File

@ -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) {