Improvements in the energy view

This commit is contained in:
Michael Zanetti 2021-12-06 00:33:02 +01:00
parent da9e5d8d7f
commit 6d049101f4
15 changed files with 976 additions and 203 deletions

View File

@ -183,9 +183,9 @@ void EnergyLogs::appendEntry(EnergyLogEntry *entry)
beginInsertRows(QModelIndex(), m_list.count(), m_list.count());
m_list.append(entry);
endInsertRows();
emit countChanged();
emit entryAdded(entry);
emit entriesAdded({entry});
emit countChanged();
}
void EnergyLogs::appendEntries(const QList<EnergyLogEntry *> &entries)

View File

@ -81,8 +81,6 @@ QString PowerBalanceLogs::logsName() const
void PowerBalanceLogs::addEntry(PowerBalanceLogEntry *entry)
{
appendEntry(entry);
if (entry->consumption() < m_minValue) {
m_minValue = entry->consumption();
emit minValueChanged();
@ -117,11 +115,12 @@ void PowerBalanceLogs::addEntry(PowerBalanceLogEntry *entry)
emit maxValueChanged();
}
appendEntry(entry);
}
EnergyLogEntry *PowerBalanceLogs::find(const QDateTime &timestamp) const
{
// qWarning() << "Finding log entry for timestamp:" << timestamp;
qWarning() << "Finding log entry for timestamp:" << timestamp;
int oldest = 0;
int newest = rowCount() - 1;
EnergyLogEntry *entry = nullptr;
@ -131,7 +130,7 @@ EnergyLogEntry *PowerBalanceLogs::find(const QDateTime &timestamp) const
EnergyLogEntry *newestEntry = get(newest);
int middle = (newest - oldest) / 2 + oldest;
EnergyLogEntry *middleEntry = get(middle);
// qWarning() << "Oldest:" << oldestEntry->timestamp().toString() << "Middle:" << middleEntry->timestamp().toString() << "Newest:" << newestEntry->timestamp().toString() << ":" << (newest - oldest);
qWarning() << "Oldest:" << oldestEntry->timestamp().toString() << "Middle:" << middleEntry->timestamp().toString() << "Newest:" << newestEntry->timestamp().toString() << ":" << (newest - oldest);
if (timestamp <= oldestEntry->timestamp()) {
return oldestEntry;
}

View File

@ -180,7 +180,7 @@ void BarSeriesAdapter::logEntryAdded(LogEntry *entry)
m_timeslots[slotIdx].entries.append(entry);
m_set->replace(slotIdx, m_timeslots[slotIdx].value());
qDebug() << "Adding entry" << entry->timestamp() << "timestlot" << timeSlotStart << "at" << slotIdx << "value" << m_timeslots[slotIdx].value();
// qDebug() << "Adding entry" << entry->timestamp() << "timestlot" << timeSlotStart << "at" << slotIdx << "value" << m_timeslots[slotIdx].value();
// if (!m_timeslots.contains(timeSlotStart)) {
// TimeSlot timeslot;

View File

@ -85,8 +85,6 @@ void ThingsProxy::setParentProxy(ThingsProxy *parentProxy)
if (!m_engine) {
return;
}
setSortRole(Things::RoleName);
sort(0);
connect(m_parentProxy, SIGNAL(countChanged()), this, SIGNAL(countChanged()));
connect(m_parentProxy, &QAbstractItemModel::dataChanged, this, [this]() {
if (m_engine) {

View File

@ -271,5 +271,6 @@
<file>ui/system/PackageListPage.qml</file>
<file>ui/mainviews/energy/StatsBase.qml</file>
<file>ui/mainviews/energy/EnergySettingsPage.qml</file>
<file>ui/mainviews/energy/ConsumersPieChart.qml</file>
</qresource>
</RCC>

View File

@ -95,7 +95,7 @@ MainViewBase {
anchors.fill: parent
anchors.margins: app.margins / 2
contentHeight: energyGrid.childrenRect.height
visible: energyMeters.count > 0
visible: !engine.thingManager.fetchingData && engine.jsonRpcClient.experiences.hasOwnProperty("Energy")
topMargin: root.topMargin
// GridLayout directly in a flickable causes problems at initialisation
@ -117,6 +117,7 @@ MainViewBase {
Layout.fillWidth: true
Layout.preferredHeight: width
energyManager: energyManager
visible: producers.count > 0
}
CurrentProductionBalancePieChart {
Layout.fillWidth: true
@ -128,6 +129,7 @@ MainViewBase {
PowerConsumptionBalanceHistory {
Layout.fillWidth: true
Layout.preferredHeight: width
visible: producers.count > 0
}
PowerProductionBalanceHistory {
@ -136,7 +138,7 @@ MainViewBase {
visible: producers.count > 0
}
ConsumersBarChart {
ConsumersPieChart {
Layout.fillWidth: true
Layout.preferredHeight: width
energyManager: energyManager
@ -144,6 +146,15 @@ MainViewBase {
colors: root.thingColors
consumers: consumers
}
// ConsumersBarChart {
// Layout.fillWidth: true
// Layout.preferredHeight: width
// energyManager: energyManager
// visible: consumers.count > 0
// colors: root.thingColors
// consumers: consumers
// }
ConsumersHistory {
Layout.fillWidth: true
Layout.preferredHeight: width
@ -157,6 +168,7 @@ MainViewBase {
Layout.preferredHeight: width
energyManager: energyManager
}
ConsumerStats {
Layout.fillWidth: true
Layout.preferredHeight: width
@ -172,12 +184,18 @@ MainViewBase {
EmptyViewPlaceholder {
anchors.centerIn: parent
width: parent.width - app.margins * 2
visible: !engine.jsonRpcClient.experiences.hasOwnProperty("Energy")
visible: !engine.thingManager.fetchingData && !engine.jsonRpcClient.experiences.hasOwnProperty("Energy")
title: qsTr("Energy plugin not installed installed.")
text: qsTr("This %1 system does not have the energy extensions installed.").arg(Configuration.systemName)
imageSource: "../images/smartmeter.svg"
buttonText: qsTr("Install energy plugin")
buttonVisible: packagesFilterModel.count > 0
onButtonClicked: pageStack.push(Qt.resolvedUrl("../system/PackageListPage.qml"), {filter: "nymea-experience-plugin-energy"})
PackagesFilterModel {
id: packagesFilterModel
packages: engine.systemController.packages
nameFilter: "nymea-experience-plugin-energy"
}
}
EmptyViewPlaceholder {
anchors.centerIn: parent

View File

@ -43,14 +43,12 @@ StatsBase {
// print("config:", config.startTime(), config.sampleList(), config.sampleListNames())
powerLogs.sampleRate = config.sampleRate
powerLogs.startTime = config.startTime()
powerLogs.sampleList = config.sampleList()
powerLogs.startTime = new Date(config.startTime().getTime() - config.sampleRate * 60000)
barSeries.clear();
barSeries.thingBarSetMap = ({})
valueAxis.max = 0
categoryAxis.categories = config.sampleListNames()
chartView.animationOptions = ChartView.SeriesAnimations
@ -66,45 +64,132 @@ StatsBase {
onFetchingDataChanged: {
if (!fetchingData) {
barSeries.clear();
for (var j = 0; j < consumers.count; j++) {
// Note: Needs to be let, not var so the lambda capture below copies it instead of capturing the reference
let consumer = consumers.get(j)
// print("ConsumerStats: Adding thing:", consumer.name)
let totalEnergyConsumedState = consumer.stateByName("totalEnergyConsumed")
// print("Adding consumer:", consumer.name, consumer.id)
var consumptionValues = []
for (var i = 0; i < sampleList.length; i++) {
var start = powerLogs.find(consumer.id, new Date(sampleList[i]))
var startValue = start !== null ? start.totalConsumption : 0
var end = i < sampleList.length -1 ? powerLogs.find(consumer.id, new Date(sampleList[i+1])) : null
var endValue = end !== null ? end.totalConsumption : 0
if (i == sampleList.length - 1) {
endValue = totalEnergyConsumedState.value
var config = root.configs[selectionTabs.currentValue.config]
// First grouping log entries by timestamp
var groupedEntries = []
var groupedEntry = {}
for (var i = powerLogs.count - 1; i >= 0; i--) {
var entry = powerLogs.get(i);
// print("grouping entry:", entry.timestamp, "current group entry", groupedEntry.timestamp, groupedEntry.hasOwnProperty("timestamp"))
if (!groupedEntry.hasOwnProperty("timestamp")) {
groupedEntry.timestamp = entry.timestamp;
// print("Starting new groupentry", groupedEntry.timestamp, entry.timestamp)
}
if (groupedEntry.timestamp.getTime() !== entry.timestamp.getTime()) {
if (groupedEntries.length > config.count) {
break;
}
// print("finalizing grouped entry", groupedEntry.timestamp)
groupedEntries.unshift(groupedEntry);
groupedEntry = {
timestamp: entry.timestamp
}
// print("Starting new groupentry", groupedEntry.timestamp, entry.timestamp)
}
groupedEntry[entry.thingId] = entry.totalConsumption
}
if (groupedEntry.hasOwnProperty("timestamp") && groupedEntries.length <= config.count) {
// print("finalizing grouped entry", groupedEntry.timestamp)
groupedEntries.unshift(groupedEntry)
}
chartView.animationOptions = ChartView.NoAnimation
var labels = []
var entries = []
var newestLogTimestamp = powerLogs.count > 0 ? powerLogs.get(powerLogs.count - 1).timestamp : new Date();
for (var i = 0; i < config.count; i++) {
var groupedEntry = groupedEntries[groupedEntries.length - i - 1]
// print("have grouped entry:", groupedEntry ? groupedEntry.timestamp : "null")
// if it's the first, let's add a generated entry which shows the total from the newest log to the current live value
if (i == 0) {
var liveEntry = {}
for (var j = 0; j < consumers.count; j++) {
var consumer = consumers.get(j)
// print("Got consumer:", consumer.id, consumer.name)
var value = consumer.stateByName("totalEnergyConsumed").value;
if (groupedEntry) {
value -= groupedEntry.hasOwnProperty(consumer.id) ? groupedEntry[consumer.id] : 0
}
liveEntry[consumer.id] = value
valueAxis.adjustMax(value)
}
// print("adding sample", new Date(sampleList[i]), start ? start.timestamp : "X", " - ", end ? end.timestamp : "X")
// print("values. start:", startValue, "end", endValue, "diff", endValue - startValue)
var consumptionValue = endValue - startValue
// print("Value", consumptionValue)
consumptionValues.push(consumptionValue)
valueAxis.adjustMax(consumptionValue)
// print("Adding live entry", JSON.stringify(liveEntry))
entries.unshift(liveEntry)
}
let barSet = barSeries.append(consumer.name, consumptionValues)
barSet.color = root.colors[j % root.colors.length]
barSet.borderWidth = 0
barSet.borderColor = barSet.color
barSeries.thingBarSetMap[consumer] = barSet
totalEnergyConsumedState.onValueChanged.connect(function() {
var sampleList = root.configs[selectionTabs.currentValue.config].sampleList()
var lastSample = sampleList[sampleList.length - 1]
// print("sampleList:", powerLogs.sampleList)
var start = powerLogs.find(consumer.id, new Date(lastSample))
// print("consumer value changed:", consumer.name, totalEnergyConsumedState.value, start.timestamp, start.totalConsumption)
var barSet = barSeries.thingBarSetMap[consumer]
barSet.replace(barSet.count - 1, totalEnergyConsumedState.value - start.totalConsumption)
})
// Add the actual entry
var graphEntry = {}
var labelTime = new Date();
if (groupedEntry) {
var previousGroupedEntry = groupedEntries[groupedEntries.length - i - 2]
for (var j = 0; j < consumers.count; j++) {
var consumer = consumers.get(j)
var value = groupedEntry.hasOwnProperty(consumer.id) ? groupedEntry[consumer.id] : 0
if (previousGroupedEntry) {
var previousValue = previousGroupedEntry.hasOwnProperty(consumer.id) ? previousGroupedEntry[consumer.id] : 0
value -= previousValue
}
graphEntry[consumer.id] = value
valueAxis.adjustMax(value)
}
labelTime = groupedEntry.timestamp
} else {
for (var j = 0; j < consumers.count; j++) {
var consumer = consumers.get(j)
graphEntry[consumer.id] = 0
}
labelTime = new Date(newestLogTimestamp.getTime() - config.sampleRate * i * 60000)
}
// print("Adding entry:", labelTime, config.toLabel(labelTime), JSON.stringify(graphEntry))
entries.unshift(graphEntry)
labels.unshift(labelTime)
// Given we've added 2 entries for the first run but only one label, we'll add the missing label
// at the end. This will shift the labels by one entries but that's ok because the logs timestamp
// is when the sample was created, but for the user it's better to show the the consumption values
// *during* that sample, not *before* the sample
if (i == config.count - 1) {
labelTime = new Date(labelTime.getTime() - config.sampleRate * 60000)
// print("Adding oldest entry label", labelTime, config.sampleRate, config.toLabel(labelTime))
labels.unshift(labelTime)
}
}
// print("assigning categories:", labels)
categoryAxis.timestamps = labels
var map = {}
for (var j = 0; j < consumers.count; j++) {
var consumer = consumers.get(j)
var barSet = barSeries.append(consumer.name, [])
barSet.color = root.colors[j % root.colors.length]
barSet.borderColor = barSet.color
barSet.borderWith = 0
map[consumer.id] = barSet
}
barSeries.thingBarSetMap = map
chartView.animationOptions = ChartView.SeriesAnimations
for (var i = 0; i < entries.length; i++) {
var entry = entries[i]
// print("Adding entry", JSON.stringify(entry))
for (var j = 0; j < consumers.count; j++) {
var consumer = consumers.get(j)
barSeries.thingBarSetMap[consumer.id].append(entry[consumer.id])
}
}
}
}
@ -143,7 +228,7 @@ StatsBase {
Layout.fillWidth: true
Layout.margins: Style.smallMargins
horizontalAlignment: Text.AlignHCenter
text: qsTr("Consumers statistics")
text: qsTr("Consumers totals")
}
@ -155,13 +240,14 @@ StatsBase {
currentIndex: 0
model: ListModel {
Component.onCompleted: {
append({modelData: qsTr("Months"), config: "months" })
append({modelData: qsTr("Weeks"), config: "weeks" })
append({modelData: qsTr("Days"), config: "days" })
append({modelData: qsTr("Hours"), config: "hours" })
append({modelData: qsTr("Days"), config: "days" })
append({modelData: qsTr("Weeks"), config: "weeks" })
append({modelData: qsTr("Months"), config: "months" })
append({modelData: qsTr("Years"), config: "years" })
// append({modelData: qsTr("Minutes"), config: "minutes" })
selectionTabs.currentIndex = 2
selectionTabs.currentIndex = 1
}
}
onCurrentValueChanged: {
@ -217,6 +303,16 @@ StatsBase {
lineVisible: false
titleVisible: false
shadesVisible: false
categories: {
var ret = []
for (var i = 0; i < timestamps.length; i++) {
ret.push(root.configs[selectionTabs.currentValue.config].toLabel(timestamps[i]))
}
return ret
}
property var timestamps: []
}
axisY: ValueAxis {
id: valueAxis
@ -238,6 +334,78 @@ StatsBase {
property var thingBarSetMap: ({})
}
MouseArea {
id: mouseArea
anchors.fill: chartView
anchors.leftMargin: chartView.plotArea.x
anchors.topMargin: chartView.plotArea.y
anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y
hoverEnabled: true
Item {
id: toolTip
property int idx: Math.floor(mouseArea.mouseX * categoryAxis.count / mouseArea.width)
visible: mouseArea.containsMouse
x: Math.min(idx * mouseArea.width / categoryAxis.count, mouseArea.width - width)
property double setMaxValue: {
var max = 0;
for (var i = 0; i < consumers.count; i++) {
var consumer = consumers.get(i)
max = barSeries.thingBarSetMap.hasOwnProperty(consumer.id) ? Math.max(max, barSeries.thingBarSetMap[consumer.id].at(idx)) : 0
}
return max
}
y: Math.min(Math.max(mouseArea.height - (setMaxValue * mouseArea.height / valueAxis.max) - height - Style.smallMargins, 0), mouseArea.height - height)
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
Rectangle {
anchors.fill: parent
color: Style.tileOverlayColor
opacity: .8
radius: Style.smallCornerRadius
}
ColumnLayout {
id: tooltipLayout
anchors {
left: parent.left
top: parent.top
margins: Style.smallMargins
}
Label {
text: categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
font: Style.smallFont
}
Repeater {
model: consumers
delegate: RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: root.colors[index % root.colors.length]
}
Label {
text: barSeries.thingBarSetMap.hasOwnProperty(model.id) ? "%1: %2 kWh".arg(model.name).arg(barSeries.thingBarSetMap[model.id].at(toolTip.idx).toFixed(2)) : ""
font: Style.extraSmallFont
}
}
}
}
}
}
}
}
}

View File

@ -141,7 +141,9 @@ ChartView {
series.borderColor = series.color
// print("Adding thingId series", thing.id, thing.name)
d.thingsSeriesMap[thing.id] = series
var map = d.thingsSeriesMap
map[thing.id] = series
d.thingsSeriesMap = map
consumerThingIds.push(thing.id)
}
thingPowerLogs.thingIds = consumerThingIds;
@ -235,4 +237,102 @@ ChartView {
consumptionUpperSeries.append(entry.timestamp.getTime(), entry.consumption)
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.leftMargin: root.plotArea.x
anchors.topMargin: root.plotArea.y
anchors.rightMargin: root.width - root.plotArea.width - root.plotArea.x
anchors.bottomMargin: root.height - root.plotArea.height - root.plotArea.y
hoverEnabled: true
Rectangle {
height: parent.height
width: 1
color: Style.foregroundColor
x: mouseArea.mouseX
visible: mouseArea.containsMouse
}
Item {
id: toolTip
visible: mouseArea.containsMouse
property int idx: consumptionUpperSeries.count - Math.floor(mouseArea.mouseX * consumptionUpperSeries.count / mouseArea.width)
property int seriesIndex: consumptionUpperSeries.count - idx
property int xOnRight: mouseArea.mouseX + Style.smallMargins
property int xOnLeft: mouseArea.mouseX - Style.smallMargins - width
x: xOnRight + width < mouseArea.width ? xOnRight : xOnLeft
property double maxValue: consumptionUpperSeries.at(seriesIndex).y
y: Math.min(Math.max(mouseArea.height - (maxValue * mouseArea.height / valueAxis.max) - height - Style.margins, 0), mouseArea.height - height)
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
property date timestamp: new Date(consumptionUpperSeries.at(seriesIndex).x)
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
Rectangle {
anchors.fill: parent
color: Style.tileOverlayColor
opacity: .8
radius: Style.smallCornerRadius
}
ColumnLayout {
id: tooltipLayout
anchors {
left: parent.left
top: parent.top
margins: Style.smallMargins
}
Label {
text: toolTip.timestamp.toLocaleString(Qt.locale(), Locale.ShortFormat)
font: Style.smallFont
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: consumptionSeries.color
}
Label {
property double rawValue: consumptionUpperSeries.at(toolTip.seriesIndex).y
property double displayValue: rawValue >= 1000 ? rawValue / 1000 : rawValue
property string unit: rawValue >= 1000 ? "kW" : "W"
text: "%1: %2 %3".arg(qsTr("Total")).arg(displayValue.toFixed(2)).arg(unit)
font: Style.extraSmallFont
}
}
Repeater {
model: consumers
delegate: RowLayout {
id: consumerToolTipDelegate
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: root.colors[index % root.colors.length]
}
Label {
property ThingPowerLogEntry entry: thingPowerLogs.find(model.id, toolTip.timestamp)
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(model.name).arg(displayValue.toFixed(2)).arg(unit)
font: Style.extraSmallFont
}
}
}
}
}
}
}

View File

@ -0,0 +1,156 @@
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
ChartView {
id: root
backgroundColor: "transparent"
animationOptions: ChartView.SeriesAnimations
title: qsTr("Consumers balance")
titleColor: Style.foregroundColor
legend.visible: false
property EnergyManager energyManager: null
property ThingsProxy consumers: null
property var colors: null
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 consumption = energyManager.currentPowerConsumption
for (var i = 0; i < consumers.count; i++) {
consumption -= consumers.get(i).stateByName("currentPower").value
}
d.unknownSlice.value = consumption
}
}
QtObject {
id: d
property var thingsColorMap: ({})
property PieSlice unknownSlice: null
}
function updateConsumers() {
consumersBalanceSeries.clear();
var unknownConsumption = energyManager.currentPowerConsumption
var colorMap = {}
for (var i = 0; i < consumers.count; i++) {
var consumer = consumers.get(i)
colorMap[consumer] = root.colors[i % root.colors.length]
let currentPowerState = consumer.stateByName("currentPower")
let slice = consumersBalanceSeries.append(consumer.name, currentPowerState.value)
slice.color = root.colors[i % root.colors.length]
currentPowerState.valueChanged.connect(function() {
slice.value = currentPowerState.value
})
unknownConsumption -= currentPowerState.value
}
d.unknownSlice = consumersBalanceSeries.append(qsTr("Unknown"), unknownConsumption)
d.unknownSlice.color = Style.gray
d.thingsColorMap = colorMap
}
PieSeries {
id: consumersBalanceSeries
size: 0.9
holeSize: 0.7
}
ColumnLayout {
id: centerLayout
x: root.plotArea.x + (root.plotArea.width - width) / 2
y: root.plotArea.y + (root.plotArea.height - height) / 2
width: root.plotArea.width * 0.65
// height: root.plotArea.height * 0.65
spacing: Style.smallMargins
property int maximumHeight: root.plotArea.height * 0.65
ColumnLayout {
Layout.fillWidth: true
spacing: 0
Label {
text: qsTr("Total")
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.bigFont
}
}
ListView {
Layout.fillWidth: true
Layout.preferredHeight: count * (Style.smallMargins + Style.extraSmallFont.pixelSize + Style.smallFont.pixelSize)
Layout.maximumHeight: centerLayout.maximumHeight - y
clip: true
spacing: Style.smallMargins
model: ThingsProxy {
id: sortedConsumers
engine: _engine
parentProxy: root.consumers
sortStateName: "currentPower"
sortOrder: Qt.DescendingOrder
}
delegate: ColumnLayout {
width: parent.width
spacing: 0
Label {
text: model.name
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
font: Style.extraSmallFont
}
Label {
property Thing consumer: sortedConsumers.get(index)
property State currentPowerState: consumer ? consumer.stateByName("currentPower") : null
property double value: currentPowerState ? currentPowerState.value : 0
color: d.thingsColorMap[consumer]
text: "%1 %2"
.arg((value / (value > 1000 ? 1000 : 1)).toFixed(1))
.arg(value > 1000 ? "kWh" : "W")
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
font: Style.smallFont
}
}
}
}
}

View File

@ -10,7 +10,7 @@ ChartView {
id: consumptionPieChart
backgroundColor: "transparent"
animationOptions: ChartView.SeriesAnimations
title: qsTr("Current power consumption balance")
title: qsTr("My energy mix")
titleColor: Style.foregroundColor
legend.visible: false

View File

@ -10,7 +10,7 @@ ChartView {
id: productionPieChart
backgroundColor: "transparent"
animationOptions: ChartView.SeriesAnimations
title: qsTr("Current power production balance")
title: qsTr("My energy production")
titleColor: Style.foregroundColor
legend.visible: false

View File

@ -25,7 +25,7 @@ StatsBase {
Layout.fillWidth: true
Layout.margins: Style.smallMargins
horizontalAlignment: Text.AlignHCenter
text: qsTr("Energy consumption statistics")
text: qsTr("Totals")
}
@ -36,54 +36,56 @@ StatsBase {
Layout.rightMargin: Style.smallMargins
model: ListModel {
Component.onCompleted: {
append({modelData: qsTr("Months"), config: "months" })
append({modelData: qsTr("Weeks"), config: "weeks" })
append({modelData: qsTr("Days"), config: "days" })
append({modelData: qsTr("Hours"), config: "hours" })
append({modelData: qsTr("Days"), config: "days" })
append({modelData: qsTr("Weeks"), config: "weeks" })
append({modelData: qsTr("Months"), config: "months" })
append({modelData: qsTr("Years"), config: "years" })
// append({modelData: qsTr("Minutes"), config: "minutes" })
selectionTabs.currentIndex = 2
selectionTabs.currentIndex = 1
}
}
onCurrentValueChanged: {
var config = root.configs[currentValue.config]
print("config:", config.startTime(), config.sampleList(), config.sampleListNames())
print("config:", config.startTime(), config.sampleRate)
powerBalanceLogs.loadingInhibited = true
powerBalanceLogs.sampleRate = config.sampleRate
powerBalanceLogs.startTime = config.startTime()
powerBalanceLogs.sampleList = config.sampleList()
powerBalanceLogs.startTime = new Date(config.startTime().getTime() - config.sampleRate * 60000)
powerBalanceLogs.loadingInhibited = false
barSeries.clear();
d.consumptionSet = barSeries.append(qsTr("Consumed"), [])
d.consumptionSet.color = Style.blue
d.consumptionSet.borderColor = d.consumptionSet.color
d.consumptionSet.borderWidth = 0
d.productionSet = barSeries.append(qsTr("Produced"), [])
d.productionSet.color = Style.green
d.productionSet.borderColor = d.productionSet.color
d.productionSet.borderWidth = 0
d.acquisitionSet = barSeries.append(qsTr("From grid"), [])
d.acquisitionSet.color = Style.red
d.acquisitionSet.borderColor = d.acquisitionSet.color
d.acquisitionSet.borderWidth = 0
d.returnSet = barSeries.append(qsTr("To grid"), [])
d.returnSet.color = Style.orange
d.returnSet.borderColor = d.returnSet.color
d.returnSet.borderWidth = 0
valueAxis.max = 0
categoryAxis.categories = config.sampleListNames()
chartView.animationOptions = ChartView.SeriesAnimations
}
}
Connections {
target: energyManager
onPowerBalanceChanged: {
var start = powerBalanceLogs.get(powerBalanceLogs.count - 1)
// print("balance changed:", d.consumptionSet, powerBalanceLogs, powerBalanceLogs.count)
// print("updating", start.timestamp, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0))
var start = powerBalanceLogs.get(powerBalanceLogs.count - 1 )
// print("balance changed:", d.consumptionSet, powerBalanceLogs, powerBalanceLogs.count)
// print("updating", start.timestamp, start.totalConsumption, root.energyManager.totalConsumption, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0))
d.consumptionSet.replace(d.consumptionSet.count - 1, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0))
d.productionSet.replace(d.productionSet.count - 1, root.energyManager.totalProduction - (start ? start.totalProduction : 0))
d.acquisitionSet.replace(d.acquisitionSet.count - 1, root.energyManager.totalAcquisition - (start ? start.totalAcquisition : 0))
@ -96,48 +98,102 @@ StatsBase {
engine: _engine
loadingInhibited: true
property var sampleList: minutesList
onFetchingDataChanged: {
if (!fetchingData) {
if (powerBalanceLogs.count == 0) {
valueAxis.adjustMax(root.energyManager.totalConsumption)
valueAxis.adjustMax(root.energyManager.totalAcquisition)
valueAxis.adjustMax(root.energyManager.totalProduction)
valueAxis.adjustMax(root.energyManager.totalReturn)
chartView.animationOptions = ChartView.NoAnimation
for (var i = 0; i < sampleList.length; i++) {
d.consumptionSet.append(i == sampleList.length - 1 ? root.energyManager.totalConsumption : 0)
d.productionSet.append(i == sampleList.length - 1 ? root.energyManager.totalProduction : 0)
d.acquisitionSet.append(i == sampleList.length - 1 ? root.energyManager.totalAcquisition : 0)
d.returnSet.append(i == sampleList.length - 1 ? root.energyManager.totalReturn : 0)
print("Logs fetched")
var config = root.configs[selectionTabs.currentValue.config]
var labels = []
var entries = []
var newestLogTimestamp = powerBalanceLogs.count > 0 ? powerBalanceLogs.get(powerBalanceLogs.count - 1).timestamp : new Date();
for (var i = 0; i < config.count; i++) {
var entry = powerBalanceLogs.get(powerBalanceLogs.count - i - 1)
// if it's the first, let's add a generated entry which shows the total from the newest log to the current live value
if (i == 0) {
var liveEntry = {
consumption: energyManager.totalConsumption,
production: energyManager.totalProduction,
acquisition: energyManager.totalAcquisition,
returned: energyManager.totalReturn
}
if (entry) {
liveEntry.consumption -= entry.totalConsumption
liveEntry.production -= entry.totalProduction
liveEntry.acquisition -= entry.totalAcquisition
liveEntry.returned -= entry.totalReturn
}
print("Adding live entry:", liveEntry.consumption, root.energyManager.totalConsumption, entry ? entry.totalConsumption : 0)
entries.unshift(liveEntry)
valueAxis.adjustMax(liveEntry.consumption)
valueAxis.adjustMax(liveEntry.production)
valueAxis.adjustMax(liveEntry.acquisition)
valueAxis.adjustMax(liveEntry.returned)
}
return;
// Add the actual entry
var graphEntry = {
consumption: 0,
production: 0,
acquisition: 0,
returned: 0
}
var labelTime = new Date();
if (entry) {
// print("Have entry:", entry.timestamp, config.toLabel(entry.timestamp))
var previous = powerBalanceLogs.get(powerBalanceLogs.count - i - 2)
if (previous) {
graphEntry.consumption = entry.totalConsumption - previous.totalConsumption
graphEntry.production = entry.totalProduction - previous.totalProduction
graphEntry.acquisition = entry.totalAcquisition - previous.totalAcquisition
graphEntry.returned = entry.totalReturn - previous.totalReturn
} else {
graphEntry.consumption = entry.totalConsumption
graphEntry.production = entry.totalProduction
graphEntry.acquisition = entry.totalAcquisition
graphEntry.returned = entry.totalReturn
}
labelTime = entry.timestamp
} else {
labelTime = new Date(newestLogTimestamp.getTime() - config.sampleRate * i * 60000)
}
// print("Adding entry:", labelTime, graphEntry.consumption, config.toLabel(labelTime))
entries.unshift(graphEntry)
labels.unshift(labelTime)
// Given we've added 2 entries for the first run but only one label, we'll add the missing label
// at the end. This will shift the labels by one entries but that's ok because the logs timestamp
// is when the sample was created, but for the user it's better to show the the consumption values
// *during* that sample, not *before* the sample
if (i == config.count - 1) {
labelTime = new Date(labelTime.getTime() - config.sampleRate * 60000)
// print("Adding oldest entry label", labelTime, config.sampleRate, config.toLabel(labelTime))
labels.unshift(labelTime)
}
valueAxis.adjustMax(graphEntry.consumption)
valueAxis.adjustMax(graphEntry.production)
valueAxis.adjustMax(graphEntry.acquisition)
valueAxis.adjustMax(graphEntry.returned)
}
for (var i = 0; i < sampleList.length; i++) {
var start = powerBalanceLogs.find(new Date(sampleList[i]))
var end = null;
if (i+1 < sampleList.length) {
end = powerBalanceLogs.find(new Date(sampleList[i+1]))
}
// print("** stats for:", new Date(sampleList[i]), /*start, end, */"start:", start ? start.totalConsumption : 0, "end:", end ? end.totalConsumption : root.energyManager.totalConsumption)
var consumptionValue = (end != null ? end.totalConsumption : root.energyManager.totalConsumption) - (start ? start.totalConsumption : 0)
var productionValue = (end != null ? end.totalProduction : root.energyManager.totalProduction) - (start ? start.totalProduction : 0)
var acquisitionValue = (end != null ? end.totalAcquisition : root.energyManager.totalAcquisition) - (start ? start.totalAcquisition : 0)
var returnValue = (end != null ? end.totalReturn : root.energyManager.totalReturn) - (start ? start.totalReturn : 0)
// print("assigning categories:", labels)
categoryAxis.timestamps = labels
valueAxis.adjustMax(consumptionValue)
valueAxis.adjustMax(productionValue)
valueAxis.adjustMax(acquisitionValue)
valueAxis.adjustMax(returnValue)
d.consumptionSet.append(consumptionValue)
d.productionSet.append(productionValue)
d.acquisitionSet.append(acquisitionValue)
d.returnSet.append(returnValue)
chartView.animationOptions = ChartView.SeriesAnimations
for (var i = 0; i < entries.length; i++) {
// print("Appending to set", JSON.stringify(entries[i]))
d.consumptionSet.append(entries[i].consumption)
d.productionSet.append(entries[i].production)
d.acquisitionSet.append(entries[i].acquisition)
d.returnSet.append(entries[i].returned)
}
}
}
@ -146,14 +202,23 @@ StatsBase {
return
}
var config = root.configs[selectionTabs.currentValue.config]
var start = entry
var consumptionValue = root.energyManager.totalConsumption - (start ? start.totalConsumption : 0)
var productionValue = root.energyManager.totalProduction - (start ? start.totalProduction : 0)
var acquisitionValue = root.energyManager.totalAcquisition - (start ? start.totalAcquisition : 0)
var returnValue = root.energyManager.totalReturn - (start ? start.totalReturn : 0)
// print("Entry added:", entry.timestamp, entry.totalConsumption, consumptionValue)
chartView.animationOptions = ChartView.NoAnimation
categoryAxis.categories = configs[selectionTabs.currentValue.config].sampleListNames()
var timestamps = categoryAxis.timestamps;
timestamps.splice(0, 1)
timestamps.push(entry.timestamp)
categoryAxis.timestamps = timestamps
d.consumptionSet.append(consumptionValue)
d.productionSet.append(productionValue)
d.acquisitionSet.append(acquisitionValue)
@ -171,7 +236,7 @@ StatsBase {
id: chartView
Layout.fillWidth: true
Layout.fillHeight: true
animationOptions: ChartView.NoAnimations
animationOptions: ChartView.NoAnimation
backgroundColor: "transparent"
legend.alignment: Qt.AlignBottom
@ -214,6 +279,17 @@ StatsBase {
lineVisible: false
titleVisible: false
shadesVisible: false
categories: {
var ret = []
for (var i = 0; i < timestamps.length; i++) {
ret.push(root.configs[selectionTabs.currentValue.config].toLabel(timestamps[i]))
}
return ret
}
property var timestamps: []
}
axisY: ValueAxis {
id: valueAxis
@ -233,6 +309,106 @@ StatsBase {
}
}
}
MouseArea {
id: mouseArea
anchors.fill: chartView
anchors.leftMargin: chartView.plotArea.x
anchors.topMargin: chartView.plotArea.y
anchors.rightMargin: chartView.width - chartView.plotArea.width - chartView.plotArea.x
anchors.bottomMargin: chartView.height - chartView.plotArea.height - chartView.plotArea.y
hoverEnabled: true
Item {
id: toolTip
property int idx: Math.floor(mouseArea.mouseX * categoryAxis.count / mouseArea.width)
visible: mouseArea.containsMouse
x: Math.min(idx * mouseArea.width / categoryAxis.count, mouseArea.width - width)
property double setMaxValue: d.consumptionSet && d.productionSet && d.acquisitionSet && d.returnSet ?
Math.max(d.consumptionSet.at(idx), Math.max(d.productionSet.at(idx), Math.max(d.acquisitionSet.at(idx), d.returnSet.at(idx))))
: 0
y: Math.min(Math.max(mouseArea.height - (setMaxValue * mouseArea.height / valueAxis.max) - height - Style.smallMargins, 0), mouseArea.height - height)
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
Rectangle {
anchors.fill: parent
color: Style.tileOverlayColor
opacity: .8
radius: Style.smallCornerRadius
}
ColumnLayout {
id: tooltipLayout
anchors {
left: parent.left
top: parent.top
margins: Style.smallMargins
}
// Label {
// text: powerBalanceLogs.count + ":" + categoryAxis.count + ":" + toolTip.idx
// }
Label {
text: categoryAxis.timestamps.length > toolTip.idx ? root.configs[selectionTabs.currentValue.config].toLongLabel(categoryAxis.timestamps[toolTip.idx]) : ""
font: Style.smallFont
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.blue
}
Label {
text: d.consumptionSet ? qsTr("Consumed: %1 kWh").arg(d.consumptionSet.at(toolTip.idx).toFixed(2)) : ""
font: Style.extraSmallFont
}
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.green
}
Label {
text: d.productionSet ? qsTr("Produced: %1 kWh").arg(d.productionSet.at(toolTip.idx).toFixed(2)) : ""
font: Style.extraSmallFont
}
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.red
}
Label {
text: d.acquisitionSet ? qsTr("From grid: %1 kWh").arg(d.acquisitionSet.at(toolTip.idx).toFixed(2)) : ""
font: Style.extraSmallFont
}
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.orange
}
Label {
text: d.returnSet ? qsTr("To grid: %1 kWh").arg(d.returnSet.at(toolTip.idx).toFixed(2)) : ""
font: Style.extraSmallFont
}
}
}
}
}
}
}
}

View File

@ -12,7 +12,7 @@ ChartView {
margins.bottom: 0
margins.top: 0
title: qsTr("Power consumption balance history")
title: qsTr("My consumption history")
titleColor: Style.foregroundColor
legend.alignment: Qt.AlignBottom
@ -229,4 +229,101 @@ ChartView {
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.leftMargin: root.plotArea.x
anchors.topMargin: root.plotArea.y
anchors.rightMargin: root.width - root.plotArea.width - root.plotArea.x
anchors.bottomMargin: root.height - root.plotArea.height - root.plotArea.y
hoverEnabled: true
Rectangle {
height: parent.height
width: 1
color: Style.foregroundColor
x: mouseArea.mouseX
visible: mouseArea.containsMouse
}
Item {
id: toolTip
visible: mouseArea.containsMouse
property int idx: consumptionUpperSeries.count - (Math.floor(mouseArea.mouseX * consumptionUpperSeries.count / mouseArea.width))
property int seriesIndex: consumptionUpperSeries.count - idx
property int xOnRight: mouseArea.mouseX + Style.smallMargins
property int xOnLeft: mouseArea.mouseX - Style.smallMargins - width
x: xOnRight + width < mouseArea.width ? xOnRight : xOnLeft
property double maxValue: consumptionUpperSeries.at(seriesIndex).y
y: Math.min(Math.max(mouseArea.height - (maxValue * mouseArea.height / valueAxis.max) - height - Style.margins, 0), mouseArea.height - height)
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
Rectangle {
anchors.fill: parent
color: Style.tileOverlayColor
opacity: .8
radius: Style.smallCornerRadius
}
ColumnLayout {
id: tooltipLayout
anchors {
left: parent.left
top: parent.top
margins: Style.smallMargins
}
Label {
text: new Date(consumptionUpperSeries.at(toolTip.seriesIndex).x).toLocaleString(Qt.locale(), Locale.ShortFormat)
font: Style.smallFont
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.green
}
Label {
text: qsTr("Self production: %1 kW").arg(selfProductionUpperSeries.at(toolTip.seriesIndex).y.toFixed(2))
font: Style.extraSmallFont
}
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.orange
}
Label {
text: qsTr("From battery: %1 kW").arg(storageUpperSeries.at(toolTip.seriesIndex).y.toFixed(2))
font: Style.extraSmallFont
}
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.red
}
Label {
text: qsTr("From grid: %1 kW").arg(acquisitionUpperSeries.at(toolTip.seriesIndex).y.toFixed(2))
font: Style.extraSmallFont
}
}
}
}
}
}

View File

@ -12,7 +12,7 @@ ChartView {
margins.bottom: 0
margins.top: 0
title: qsTr("Power production balance history")
title: qsTr("My production history")
titleColor: Style.foregroundColor
legend.alignment: Qt.AlignBottom
@ -222,4 +222,101 @@ ChartView {
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.leftMargin: root.plotArea.x
anchors.topMargin: root.plotArea.y
anchors.rightMargin: root.width - root.plotArea.width - root.plotArea.x
anchors.bottomMargin: root.height - root.plotArea.height - root.plotArea.y
hoverEnabled: true
Rectangle {
height: parent.height
width: 1
color: Style.foregroundColor
x: mouseArea.mouseX
visible: mouseArea.containsMouse
}
Item {
id: toolTip
visible: mouseArea.containsMouse
property int idx: productionUpperSeries.count - Math.floor(mouseArea.mouseX * productionUpperSeries.count / mouseArea.width)
property int seriesIndex: productionUpperSeries.count - idx
property int xOnRight: mouseArea.mouseX + Style.smallMargins
property int xOnLeft: mouseArea.mouseX - Style.smallMargins - width
x: xOnRight + width < mouseArea.width ? xOnRight : xOnLeft
property double maxValue: productionUpperSeries.at(seriesIndex).y
y: Math.min(Math.max(mouseArea.height - (maxValue * mouseArea.height / valueAxis.max) - height - Style.margins, 0), mouseArea.height - height)
width: tooltipLayout.implicitWidth + Style.smallMargins * 2
height: tooltipLayout.implicitHeight + Style.smallMargins * 2
Behavior on x { NumberAnimation { duration: Style.animationDuration } }
Behavior on y { NumberAnimation { duration: Style.animationDuration } }
Behavior on width { NumberAnimation { duration: Style.animationDuration } }
Behavior on height { NumberAnimation { duration: Style.animationDuration } }
Rectangle {
anchors.fill: parent
color: Style.tileOverlayColor
opacity: .8
radius: Style.smallCornerRadius
}
ColumnLayout {
id: tooltipLayout
anchors {
left: parent.left
top: parent.top
margins: Style.smallMargins
}
Label {
text: new Date(selfConsumptionUpperSeries.at(toolTip.seriesIndex).x).toLocaleString(Qt.locale(), Locale.ShortFormat)
font: Style.smallFont
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.green
}
Label {
text: qsTr("consumed: %1 kW").arg(selfConsumptionUpperSeries.at(toolTip.seriesIndex).y.toFixed(2))
font: Style.extraSmallFont
}
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.orange
}
Label {
text: qsTr("To battery: %1 kW").arg(storageUpperSeries.at(toolTip.seriesIndex).y.toFixed(2))
font: Style.extraSmallFont
}
}
RowLayout {
Rectangle {
width: Style.extraSmallFont.pixelSize
height: width
color: Style.red
}
Label {
text: qsTr("To grid: %1 kW").arg(acquisitionUpperSeries.at(toolTip.seriesIndex).y.toFixed(2))
font: Style.extraSmallFont
}
}
}
}
}
}

View File

@ -4,42 +4,55 @@ import Nymea 1.0
Item {
id: root
property int minutesCount: 10
property int hoursCount: 12
property int daysCount: 7
property int minutesCount: 9
property int hoursCount: 11
property int daysCount: 6
property int weeksCount: 12
property int monthsCount: 12
property int monthsCount: 11
property int yearsCount: 5
property var configs: ({
minutes: {
count: minutesCount,
startTime: minutesStart,
sampleRate: EnergyLogs.SampleRate1Min,
sampleList: minutesList,
sampleListNames: minutesListNames
toLabel: minuteLabel,
toLongLabel: minuteLongLabel
},
hours: {
count: hoursCount,
startTime: hoursStart,
sampleRate: EnergyLogs.SampleRate1Hour,
sampleList: hoursList,
sampleListNames: hoursListNames
toLabel: hourLabel,
toLongLabel: hourLongLabel
},
days: {
count: daysCount,
startTime: daysStart,
sampleRate: EnergyLogs.SampleRate1Day,
sampleList: daysList,
sampleListNames: daysListNames
toLabel: dayLabel,
toLongLabel: dayLongLabel
},
weeks: {
count: weeksCount,
startTime: weeksStart,
sampleRate: EnergyLogs.SampleRate1Week,
sampleList: weeksList,
sampleListNames: weeksListNames
toLabel: weekLabel,
toLongLabel: weekLongLabel
},
months: {
count: monthsCount,
startTime: monthsStart,
sampleRate: EnergyLogs.SampleRate1Month,
sampleList: monthsList,
sampleListNames: monthsListNames
toLabel: monthLabel,
toLongLabel: monthLongLabel
},
years: {
count: yearsCount,
startTime: yearStart,
sampleRate: EnergyLogs.SampleRate1Year,
toLabel: yearLabel,
toLongLabel: yearLabel
}
})
@ -48,45 +61,24 @@ Item {
d.setMinutes(d.getMinutes() - minutesCount + 1, 0, 0)
return d;
}
function minutesList() {
var ret = []
var startTime = minutesStart();
for (var i = 0; i < minutesCount; i++) {
var last = new Date(startTime)
ret.push(last.setTime(last.getTime() + i * 60 * 1000))
}
return ret;
function minuteLabel(date) {
return date.toLocaleString(Qt.locale(), "hh:mm")
}
function minutesListNames() {
var ret = []
var list = minutesList()
for (var i = 0; i < list.length; i++) {
ret.push(new Date(list[i]).toLocaleString(Qt.locale(), "hh:mm"))
}
return ret;
function minuteLongLabel(date) {
return date.toLocaleString(Qt.locale(), Locale.ShortFormat)
}
function hoursStart() {
var d = new Date();
d.setHours(d.getHours() - hoursCount + 1, 0, 0, 0)
return d;
}
function hoursList() {
var ret = []
var startTime = hoursStart();
for (var i = 0; i < hoursCount; i++) {
var last = new Date(startTime)
ret.push(last.setTime(last.getTime() + i * 60 * 60 * 1000))
}
return ret;
function hourLabel(date) {
return date.toLocaleString(Qt.locale(), "hh")
}
function hoursListNames() {
var ret = [];
var list = hoursList();
for (var i = 0; i < list.length; i++) {
ret.push(new Date(list[i]).toLocaleString(Qt.locale(), "hh"));
}
return ret;
function hourLongLabel(date) {
return date.toLocaleString(Qt.locale(), Locale.ShortFormat)
}
function daysStart() {
@ -95,24 +87,11 @@ Item {
d.setDate(d.getDate() - daysCount + 1);
return d;
}
function daysList() {
var ret = []
var startTime = daysStart();
for (var i = 0; i < daysCount; i++) {
var last = new Date(startTime)
ret.push(last.setDate(last.getDate() + i))
}
return ret;
function dayLabel(date) {
return date.toLocaleString(Qt.locale(), "ddd")
}
function daysListNames() {
var ret = []
var list = daysList();
for (var i = 0; i < list.length; i++) {
ret.push(new Date(list[i]).toLocaleString(Qt.locale(), "ddd"))
}
return ret;
function dayLongLabel(date) {
return date.toLocaleDateString(Qt.locale(), Locale.ShortFormat)
}
function weeksStart() {
@ -121,57 +100,41 @@ Item {
d.setDate(d.getDate() - d.getDay() - weeksCount * 7);
return d
}
function weeksList() {
var ret = []
var startTime = weeksStart();
for (var i = 0; i < weeksCount; i++) {
var last = new Date(startTime)
ret.push(last.setDate(last.getDate() + i * 7))
}
return ret;
function weekLabel(date) {
var yearStart = new Date();
yearStart.setHours(0,0,0,0);
yearStart.setDate(1);
yearStart.setMonth(0);
return Math.ceil((((date - yearStart) / 86400000) + 1)/7)
}
function weeksListNames() {
var ret = []
var list = weeksList();
for (var i = 0; i < list.length; i++) {
var d = new Date(list[i])
var dayNum = d.getDay() || 7;
d.setDate(d.getDate() + 4 - dayNum);
ret.push(Math.ceil((((d - yearStart()) / 86400000) + 1)/7))
}
return ret;
function weekLongLabel(date) {
var endDate = new Date(date)
endDate.setDate(endDate.getDate() + 6)
return date.toLocaleDateString(Qt.locale(), Locale.ShortFormat) + " - " + endDate.toLocaleDateString(Qt.locale(), Locale.ShortFormat)
}
function monthsStart() {
var d = new Date();
d.setHours(0,0,0,0);
d.setMonth(d.getMonth() - monthsCount + 1, 1);
return d;
}
function monthsList() {
var ret = []
var startTime = monthsStart();
for (var i = 0; i < monthsCount; i++) {
var last = new Date(startTime)
ret.push(last.setMonth(last.getMonth() + i))
}
return ret;
function monthLabel(date) {
return date.toLocaleString(Qt.locale(), "MMM")
}
function monthsListNames() {
var ret = []
var list = monthsList();
for (var i = 0; i < list.length; i++) {
ret.push(new Date(list[i]).toLocaleString(Qt.locale(), "MMM"))
}
return ret;
function monthLongLabel(date) {
return date.toLocaleString(Qt.locale(), "MMMM yyyy")
}
function yearStart() {
var d = new Date();
d.setHours(0,0,0,0);
d.setDate(1);
d.setMonth(0);
d.setFullYear(d.getFullYear() - yearsCount + 1, 0, 1)
return d;
}
function yearLabel(date) {
return date.toLocaleString(Qt.locale(), "yyyy")
}
}