This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
powersync-app/nymea-app/ui/mainviews/energy/ConsumersPieChart.qml
2022-06-28 15:07:18 +02:00

240 lines
8.7 KiB
QML

import QtQuick 2.8
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.2
import QtGraphicalEffects 1.0
import QtCharts 2.2
import Nymea 1.0
import "qrc:/ui/components"
ChartView {
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
readonly property Thing rootMeter: engine.thingManager.fetchingData ? null : engine.thingManager.things.getThing(energyManager.rootMeterId)
onRootMeterChanged: updateConsumers()
Connections {
target: engine.thingManager
onFetchingDataChanged: {
if (!engine.thingManager.fetchingData) {
updateConsumers()
}
}
}
Connections {
target: root.consumers
onCountChanged: {
if (!engine.thingManager.fetchingData) {
updateConsumers()
}
}
}
Connections {
target: energyManager
onPowerBalanceChanged: {
var consumersSummation = 0
for (var i = 0; i < consumers.count; i++) {
consumersSummation += consumers.get(i).stateByName("currentPower").value
}
d.consumersSummation = consumersSummation;
if (d.unknownSlice) {
d.unknownSlice.value = Math.max(0, energyManager.currentPowerConsumption - consumersSummation)
}
}
}
Component.onCompleted: updateConsumers()
QtObject {
id: d
property var thingsColorMap: ({})
property PieSlice unknownSlice: null
property double consumersSummation: 0
}
function updateConsumers() {
root.animationOptions = ChartView.NoAnimation
print("clearing consumers pie chart", consumersBalanceSeries.count)
consumersBalanceSeries.clear();
print("cleared consumers pie chart")
if (engine.thingManager.fetchingData) {
return;
}
var colorMap = {}
var consumersSummation = 0;
for (var i = 0; i < consumers.count; i++) {
var consumer = consumers.get(i)
let currentPowerState = consumer.stateByName("currentPower")
let slice = consumersBalanceSeries.append(consumer.name, currentPowerState.value)
slice.color = NymeaUtils.generateColor(Style.generationBaseColor, i)
slice.borderWidth = 0
slice.borderColor = slice.color
colorMap[consumer] = slice.color
currentPowerState.valueChanged.connect(function() {
slice.value = currentPowerState.value
})
consumersSummation += currentPowerState.value
}
d.consumersSummation = consumersSummation
if (root.rootMeter) {
var unknownConsumption = Math.max(0, energyManager.currentPowerConsumption - consumersSummation)
print("Unknown consumption:", unknownConsumption, "consumption balance", energyManager.currentPowerConsumption, "consumers summation:", consumersSummation)
d.unknownSlice = consumersBalanceSeries.append(qsTr("Unknown"), unknownConsumption)
d.unknownSlice.color = Style.gray
d.unknownSlice.borderColor = Style.gray
d.unknownSlice.borderWidth = 0
}
d.thingsColorMap = colorMap
root.animationOptions = Qt.binding(function() {
return root.animationsEnabled ? NymeaUtils.chartsAnimationOptions : ChartView.NoAnimation
})
}
PieSeries {
id: consumersBalanceSeries
size: 0.88
holeSize: 0.7
}
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
ColumnLayout {
id: contentColumn
width: parent.width
spacing: Style.smallMargins
ColumnLayout {
Layout.fillWidth: true
spacing: 0
visible: root.rootMeter
Label {
text: qsTr("Total")
font: Style.smallFont
Layout.topMargin: Style.smallMargins
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
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 ? "kWh" : "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" }
}
}
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
}
}