More work and fixes on the energy dashboard

This commit is contained in:
Michael Zanetti 2021-11-29 14:32:23 +01:00
parent 6f04164513
commit 3e31724f90
20 changed files with 871 additions and 597 deletions

View File

@ -84,32 +84,6 @@ void EnergyLogs::setSampleRate(SampleRate sampleRate)
}
}
bool EnergyLogs::fetchPowerBalance() const
{
return m_fetchPowerBalance;
}
void EnergyLogs::setFetchPowerBalance(bool fetchPowerBalance)
{
if (m_fetchPowerBalance != fetchPowerBalance) {
m_fetchPowerBalance = fetchPowerBalance;
emit fetchPowerBalanceChanged();
}
}
QList<QUuid> EnergyLogs::thingIds() const
{
return m_thingIds;
}
void EnergyLogs::setThingIds(const QList<QUuid> &thingIds)
{
if (m_thingIds != thingIds) {
m_thingIds = thingIds;
emit thingIdsChanged();
}
}
QDateTime EnergyLogs::startTime() const
{
return m_startTime;
@ -118,7 +92,6 @@ QDateTime EnergyLogs::startTime() const
void EnergyLogs::setStartTime(const QDateTime &startTime)
{
if (m_startTime != startTime) {
qCDebug(dcEnergyLogs()) << "Setting startTime";
m_startTime = startTime;
emit startTimeChanged();
}

View File

@ -57,12 +57,6 @@ public:
SampleRate sampleRate() const;
void setSampleRate(SampleRate sampleRate);
bool fetchPowerBalance() const;
void setFetchPowerBalance(bool fetchPowerBalance);
QList<QUuid> thingIds() const;
void setThingIds(const QList<QUuid> &thingIds);
QDateTime startTime() const;
void setStartTime(const QDateTime &startTime);

View File

@ -126,7 +126,7 @@ EnergyLogEntry *PowerBalanceLogs::find(const QDateTime &timestamp) const
int newest = rowCount() - 1;
EnergyLogEntry *entry = nullptr;
int step = 0;
while (oldest < newest && step < rowCount()) {
while (oldest <= newest && step < rowCount()) {
EnergyLogEntry *oldestEntry = get(oldest);
EnergyLogEntry *newestEntry = get(newest);
int middle = (newest - oldest) / 2 + oldest;
@ -155,7 +155,6 @@ EnergyLogEntry *PowerBalanceLogs::find(const QDateTime &timestamp) const
step++;
}
return entry;
}
void PowerBalanceLogs::logEntriesReceived(const QVariantMap &params)
@ -172,7 +171,7 @@ void PowerBalanceLogs::logEntriesReceived(const QVariantMap &params)
double totalAcquisition = map.value("totalAcquisition").toDouble();
double totalReturn = map.value("totalReturn").toDouble();
PowerBalanceLogEntry *entry = new PowerBalanceLogEntry(timestamp, consumption, production, acquisition, storage, totalConsumption, totalProduction, totalAcquisition, totalReturn, this);
qCritical() << "Adding entry:" << entry->timestamp() << entry->totalConsumption();
// qCritical() << "Adding entry:" << entry->timestamp() << entry->totalConsumption();
addEntry(entry);
}

View File

@ -73,15 +73,17 @@ EnergyLogEntry *ThingPowerLogs::find(const QUuid &thingId, const QDateTime &time
{
// TODO: Can we do a binary search even if they key we're looking for is not unique (but still sorted)?
// For now, 365 * consumers items is the max we'll have here which seems on the edge for doing a stupid linear search...
// qWarning() << "Finding item for" << thingId.toString() << timestamp.toString();
for (int i = rowCount() - 1; i >= 0; i--) {
ThingPowerLogEntry *entry = static_cast<ThingPowerLogEntry*>(get(i));
if (entry->thingId() != thingId) {
continue;
}
// qWarning() << "comparing" << entry->timestamp().toString();
if (timestamp == entry->timestamp()) {
return entry;
}
if (timestamp < entry->timestamp()) {
if (timestamp > entry->timestamp()) {
return nullptr; // Giving up, entry is not here
}
}
@ -130,6 +132,7 @@ void ThingPowerLogs::logEntriesReceived(const QVariantMap &params)
double totalConsumption = map.value("totalConsumption").toDouble();
double totalProduction = map.value("totalProduction").toDouble();
ThingPowerLogEntry *entry = new ThingPowerLogEntry(timestamp, thingId, currentPower, totalConsumption, totalProduction, this);
// qWarning() << "Adding entry:" << entry->thingId() << entry->timestamp().toString() << entry->totalConsumption();
if (groupForTimestamp.isEmpty()) {
groupForTimestamp.append(entry);
@ -156,6 +159,9 @@ void ThingPowerLogs::notificationReceived(const QVariantMap &data)
QVariantMap map = params.value("thingPowerLogEntry").toMap();
QDateTime timestamp = QDateTime::fromSecsSinceEpoch(map.value("timestamp").toLongLong());
QUuid thingId = map.value("thingId").toUuid();
if (!m_thingIds.isEmpty() && !m_thingIds.contains(thingId)) {
return;
}
double currentPower = map.value("currentPower").toDouble();
double totalConsumption = map.value("totalConsumption").toDouble();
double totalProduction = map.value("totalProduction").toDouble();

View File

@ -233,10 +233,14 @@ LogEntry *LogsModel::get(int index) const
LogEntry *LogsModel::findClosest(const QDateTime &dateTime)
{
qWarning() << "********************Finding closest for:" << dateTime.time().toString();
if (m_list.isEmpty()) {
return nullptr;
}
int newest = 0;
int oldest = m_list.count() - 1;
LogEntry *entry = nullptr;
int step = 0;
LogEntry *allTimeOldestEntry = m_list.at(oldest);
if (dateTime < allTimeOldestEntry->timestamp()) {
return nullptr;

View File

@ -98,7 +98,7 @@ bool PackagesFilterModel::filterAcceptsRow(int source_row, const QModelIndex &so
}
}
if (!m_nameFilter.isEmpty()) {
if (!m_packages->get(source_row)->displayName().contains(m_nameFilter)) {
if (!m_packages->get(source_row)->displayName().toLower().contains(m_nameFilter.toLower())) {
return false;
}
}

View File

@ -164,6 +164,7 @@ void TagsManager::getTagsResponse(int /*commandId*/, const QVariantMap &params)
m_tags->addTags(tags);
m_busy = false;
qWarning() << "Tags busy changed to false";
emit busyChanged();
}

View File

@ -80,7 +80,7 @@ private:
JsonRpcClient *m_jsonClient = nullptr;
Tags *m_tags = nullptr;
bool m_busy = false;
bool m_busy = true;
};
#endif // TAGSMANAGER_H

View File

@ -56,7 +56,7 @@ void ThingsProxy::setEngine(Engine *engine)
return;
}
connect(m_engine->tagsManager()->tags(), &Tags::countChanged, this, &ThingsProxy::invalidateFilter);
connect(m_engine->tagsManager()->tags(), &Tags::countChanged, this, &ThingsProxy::invalidateFilterInternal);
if (!sourceModel()) {
setSourceModel(m_engine->thingManager()->things());
@ -65,10 +65,8 @@ void ThingsProxy::setEngine(Engine *engine)
sort(0, sortOrder());
connect(sourceModel(), SIGNAL(countChanged()), this, SIGNAL(countChanged()));
connect(sourceModel(), &QAbstractItemModel::dataChanged, this, [this]() {
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
});
}
}
}
@ -92,8 +90,7 @@ void ThingsProxy::setParentProxy(ThingsProxy *parentProxy)
connect(m_parentProxy, SIGNAL(countChanged()), this, SIGNAL(countChanged()));
connect(m_parentProxy, &QAbstractItemModel::dataChanged, this, [this]() {
if (m_engine) {
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
});
@ -116,8 +113,7 @@ void ThingsProxy::setFilterTagId(const QString &filterTag)
if (m_filterTagId != filterTag) {
m_filterTagId = filterTag;
emit filterTagIdChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -131,8 +127,35 @@ void ThingsProxy::setFilterTagValue(const QString &tagValue)
if (m_filterTagValue != tagValue) {
m_filterTagValue = tagValue;
emit filterTagValueChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
QString ThingsProxy::hideTagId() const
{
return m_hideTagId;
}
void ThingsProxy::setHideTagId(const QString &tagId)
{
if (m_hideTagId != tagId) {
m_hideTagId = tagId;
emit hideTagIdChanged();
invalidateFilterInternal();
}
}
QString ThingsProxy::hideTagValue() const
{
return m_hideTagValue;
}
void ThingsProxy::setHideTagValue(const QString &tagValue)
{
if (m_hideTagValue != tagValue) {
m_hideTagValue = tagValue;
emit hideTagValueChanged();
invalidateFilterInternal();
}
}
@ -146,8 +169,7 @@ void ThingsProxy::setFilterThingId(const QString &filterThingId)
if (m_filterThingId != filterThingId) {
m_filterThingId = filterThingId;
emit filterThingIdChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -161,8 +183,7 @@ void ThingsProxy::setShownInterfaces(const QStringList &shownInterfaces)
if (m_shownInterfaces != shownInterfaces) {
m_shownInterfaces = shownInterfaces;
emit shownInterfacesChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -176,8 +197,7 @@ void ThingsProxy::setHiddenInterfaces(const QStringList &hiddenInterfaces)
if (m_hiddenInterfaces != hiddenInterfaces) {
m_hiddenInterfaces = hiddenInterfaces;
emit hiddenInterfacesChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -191,8 +211,7 @@ void ThingsProxy::setNameFilter(const QString &nameFilter)
if (m_nameFilter != nameFilter) {
m_nameFilter = nameFilter;
emit nameFilterChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -206,8 +225,7 @@ void ThingsProxy::setShownThingClassIds(const QList<QUuid> &shownThingClassIds)
if (m_shownThingClassIds != shownThingClassIds) {
m_shownThingClassIds = shownThingClassIds;
emit shownThingClassIdsChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -222,8 +240,7 @@ void ThingsProxy::setHiddenThingClassIds(const QList<QUuid> &hiddenThingClassIds
if (m_hiddenThingClassIds != hiddenThingClassIds) {
m_hiddenThingClassIds = hiddenThingClassIds;
emit hiddenThingClassIdsChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -237,8 +254,7 @@ void ThingsProxy::setRequiredEventName(const QString &requiredEventName)
if (m_requiredEventName != requiredEventName) {
m_requiredEventName = requiredEventName;
emit requiredEventNameChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -252,8 +268,7 @@ void ThingsProxy::setRequiredStateName(const QString &requiredStateName)
if (m_requiredStateName != requiredStateName) {
m_requiredStateName = requiredStateName;
emit requiredStateNameChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -267,8 +282,7 @@ void ThingsProxy::setRequiredActionName(const QString &requiredActionName)
if (m_requiredActionName != requiredActionName) {
m_requiredActionName = requiredActionName;
emit requiredActionNameChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -282,8 +296,7 @@ void ThingsProxy::setShowDigitalInputs(bool showDigitalInputs)
if (m_showDigitalInputs != showDigitalInputs) {
m_showDigitalInputs = showDigitalInputs;
emit showDigitalInputsChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -297,8 +310,7 @@ void ThingsProxy::setShowDigitalOutputs(bool showDigitalOutputs)
if (m_showDigitalOutputs != showDigitalOutputs) {
m_showDigitalOutputs = showDigitalOutputs;
emit showDigitalOutputsChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -312,8 +324,7 @@ void ThingsProxy::setShowAnalogInputs(bool showAnalogInputs)
if (m_showAnalogInputs != showAnalogInputs) {
m_showAnalogInputs = showAnalogInputs;
emit showAnalogInputsChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -327,8 +338,7 @@ void ThingsProxy::setShowAnalogOutputs(bool showAnalogOutputs)
if (m_showAnalogOutputs != showAnalogOutputs) {
m_showAnalogOutputs = showAnalogOutputs;
emit showAnalogOutputsChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -342,8 +352,7 @@ void ThingsProxy::setFilterBatteryCritical(bool filterBatteryCritical)
if (m_filterBatteryCritical != filterBatteryCritical) {
m_filterBatteryCritical = filterBatteryCritical;
emit filterBatteryCriticalChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -357,8 +366,7 @@ void ThingsProxy::setFilterDisconnected(bool filterDisconnected)
if (m_filterDisconnected != filterDisconnected) {
m_filterDisconnected = filterDisconnected;
emit filterDisconnectedChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -372,8 +380,7 @@ void ThingsProxy::setFilterSetupFailed(bool filterSetupFailed)
if (m_filterSetupFailed != filterSetupFailed) {
m_filterSetupFailed = filterSetupFailed;
emit filterSetupFailedChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -387,8 +394,7 @@ void ThingsProxy::setFilterUpdates(bool filterUpdates)
if (m_filterUpdates != filterUpdates) {
m_filterUpdates = filterUpdates;
emit filterUpdatesChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -403,8 +409,7 @@ void ThingsProxy::setParamsFilter(const QVariantMap &paramsFilter)
m_paramsFilter = paramsFilter;
emit paramsFilterChanged();
invalidateFilter();
emit countChanged();
invalidateFilterInternal();
}
}
@ -477,6 +482,15 @@ int ThingsProxy::indexOf(Thing *thing) const
return mapFromSource(sourceIndex).row();
}
void ThingsProxy::invalidateFilterInternal()
{
int oldCount = rowCount();
invalidateFilter();
if (oldCount != rowCount()) {
emit countChanged();
}
}
Thing *ThingsProxy::getInternal(int source_index) const
{
Things* d = qobject_cast<Things*>(sourceModel());
@ -543,11 +557,22 @@ bool ThingsProxy::filterAcceptsRow(int source_row, const QModelIndex &source_par
return false;
}
}
if (!m_hideTagId.isEmpty()) {
Tag *tag = m_engine->tagsManager()->tags()->findThingTag(thing->id().toString(), m_hideTagId);
if (tag && m_hideTagValue.isEmpty()) {
return false;
}
if (tag && tag->value() == m_hideTagValue) {
return false;
}
}
if (!m_filterThingId.isEmpty()) {
if (thing->id() != QUuid(m_filterThingId)) {
return false;
}
}
ThingClass *thingClass = m_engine->thingManager()->thingClasses()->getThingClass(thing->thingClassId());
// qDebug() << "Checking thing" << thingClass->name() << thingClass->interfaces();
if (!m_shownInterfaces.isEmpty()) {

View File

@ -48,6 +48,8 @@ class ThingsProxy : public QSortFilterProxyModel
Q_PROPERTY(QString filterTagId READ filterTagId WRITE setFilterTagId NOTIFY filterTagIdChanged)
Q_PROPERTY(QString filterTagValue READ filterTagValue WRITE setFilterTagValue NOTIFY filterTagValueChanged)
Q_PROPERTY(QString filterThingId READ filterThingId WRITE setFilterThingId NOTIFY filterThingIdChanged)
Q_PROPERTY(QString hideTagId READ hideTagId WRITE setHideTagId NOTIFY hideTagIdChanged)
Q_PROPERTY(QString hideTagValue READ hideTagValue WRITE setHideTagValue NOTIFY hideTagValueChanged)
Q_PROPERTY(QStringList shownInterfaces READ shownInterfaces WRITE setShownInterfaces NOTIFY shownInterfacesChanged)
Q_PROPERTY(QStringList hiddenInterfaces READ hiddenInterfaces WRITE setHiddenInterfaces NOTIFY hiddenInterfacesChanged)
Q_PROPERTY(QString nameFilter READ nameFilter WRITE setNameFilter NOTIFY nameFilterChanged)
@ -101,6 +103,12 @@ public:
QString filterTagValue() const;
void setFilterTagValue(const QString &tagValue);
QString hideTagId() const;
void setHideTagId(const QString &tagId);
QString hideTagValue() const;
void setHideTagValue(const QString &tagValue);
QString filterThingId() const;
void setFilterThingId(const QString &filterThingId);
@ -172,6 +180,8 @@ signals:
void parentProxyChanged();
void filterTagIdChanged();
void filterTagValueChanged();
void hideTagIdChanged();
void hideTagValueChanged();
void filterThingIdChanged();
void shownInterfacesChanged();
void hiddenInterfacesChanged();
@ -195,6 +205,9 @@ signals:
void sortOrderChanged();
void countChanged();
private slots:
void invalidateFilterInternal();
private:
Thing *getInternal(int source_index) const;
@ -202,6 +215,8 @@ private:
ThingsProxy *m_parentProxy = nullptr;
QString m_filterTagId;
QString m_filterTagValue;
QString m_hideTagId;
QString m_hideTagValue;
QString m_filterThingId;
QStringList m_shownInterfaces;
QStringList m_hiddenInterfaces;

View File

@ -269,5 +269,7 @@
<file>ui/mainviews/energy/CurrentConsumptionBalancePieChart.qml</file>
<file>ui/mainviews/energy/ConsumerStats.qml</file>
<file>ui/system/PackageListPage.qml</file>
<file>ui/mainviews/energy/StatsBase.qml</file>
<file>ui/mainviews/energy/EnergySettingsPage.qml</file>
</qresource>
</RCC>

View File

@ -110,6 +110,8 @@ Item {
property color darkGray: "darkGray"
property color blue: "deepskyblue"
property color orange: "#f6a625"
property color purple: "#6d5fd5"
property color lime: "#99ca53"
readonly property int fastAnimationDuration: 100
readonly property int animationDuration: 150

View File

@ -44,11 +44,25 @@ MainViewBase {
contentY: flickable.contentY + topMargin
headerButtons: [
{
iconSource: "/ui/images/configure.svg",
color: Style.iconColor,
trigger: function() {
pageStack.push("energy/EnergySettingsPage.qml", {energyManager: energyManager});
},
visible: true
}
]
EnergyManager {
id: energyManager
engine: _engine
}
property var thingColors: [Style.blue, Style.green, Style.red, Style.yellow, Style.purple, Style.yellow, Style.lime]
ThingsProxy {
id: energyMeters
engine: _engine
@ -60,6 +74,7 @@ MainViewBase {
id: consumers
engine: _engine
shownInterfaces: ["smartmeterconsumer"]
hideTagId: "hiddenInEnergyView"
}
ThingsProxy {
@ -97,57 +112,60 @@ MainViewBase {
columnSpacing: 0
// CurrentConsumptionBalancePieChart {
// Layout.fillWidth: true
// Layout.preferredHeight: width
// energyManager: energyManager
// visible: producers.count > 0
// }
// CurrentProductionBalancePieChart {
// Layout.fillWidth: true
// Layout.preferredHeight: width
// energyManager: energyManager
// visible: producers.count > 0
// }
CurrentConsumptionBalancePieChart {
Layout.fillWidth: true
Layout.preferredHeight: width
energyManager: energyManager
}
CurrentProductionBalancePieChart {
Layout.fillWidth: true
Layout.preferredHeight: width
energyManager: energyManager
visible: producers.count > 0
}
// PowerConsumptionBalanceHistory {
// Layout.fillWidth: true
// Layout.preferredHeight: width
// visible: producers.count > 0
// }
PowerConsumptionBalanceHistory {
Layout.fillWidth: true
Layout.preferredHeight: width
}
// PowerProductionBalanceHistory {
// Layout.fillWidth: true
// Layout.preferredHeight: width
// visible: producers.count > 0
// }
PowerProductionBalanceHistory {
Layout.fillWidth: true
Layout.preferredHeight: width
visible: producers.count > 0
}
// ConsumersBarChart {
// Layout.fillWidth: true
// Layout.preferredHeight: width
// energyManager: energyManager
// visible: consumers.count > 0
// }
// ConsumersHistory {
// Layout.fillWidth: true
// Layout.preferredHeight: width
// visible: consumers.count > 0
// }
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
visible: consumers.count > 0
colors: root.thingColors
consumers: consumers
}
PowerBalanceStats {
Layout.fillWidth: true
Layout.preferredHeight: width
energyManager: energyManager
}
// ConsumerStats {
// Layout.fillWidth: true
// Layout.preferredHeight: width
// energyManager: energyManager
// visible: consumers.count > 0
// }
ConsumerStats {
Layout.fillWidth: true
Layout.preferredHeight: width
energyManager: energyManager
visible: consumers.count > 0
colors: root.thingColors
consumers: consumers
}
}
}
}
EmptyViewPlaceholder {

View File

@ -3,180 +3,241 @@ import QtQuick.Layouts 1.2
import QtQuick.Controls 2.3
import QtCharts 2.3
import Nymea 1.0
import "qrc:/ui/components/"
ChartView {
StatsBase {
id: root
backgroundColor: "transparent"
legend.alignment: Qt.AlignBottom
legend.font: Style.extraSmallFont
legend.labelColor: !powerLogs.fetchingData && powerLogs.count > 0 ? Style.foregroundColor : Style.gray
// margins.left: 0
margins.right: 0
margins.bottom: 0
margins.top: 0
title: qsTr("Consumer statistics")
titleColor: Style.foregroundColor
property EnergyManager energyManager: null
property var colors: null
property ThingsProxy consumers: null
readonly property date dayStart: {
var d = new Date();
d.setHours(0,0,0,0);
return d;
Connections {
target: consumers
onCountChanged: root.update()
}
readonly property var daysList: {
var ret = []
for (var i = 6; i >= 0; i--) {
var last = new Date(dayStart)
ret.push(last.setDate(last.getDate() - i))
}
return ret;
}
readonly property var daysListNames: {
var ret = []
for (var i = 0; i < daysList.length; i++) {
ret.push(new Date(daysList[i]).toLocaleString(Qt.locale(), "ddd"))
}
return ret;
}
readonly property date weekStart: {
var d = new Date();
d.setHours(0, 0, 0, 0);
d.setDate(d.getDate() - d.getDay());
return d
}
readonly property date monthStart: {
var d = new Date();
d.setHours(0,0,0,0);
d.setDate(1);
return d;
}
readonly property date yearStart: {
var d = new Date();
d.setHours(0,0,0,0);
d.setDate(1);
d.setMonth(0);
return d;
}
ThingsProxy {
id: consumers
engine: _engine
shownInterfaces: ["smartmeterconsumer"]
sortStateName: "totalEnergyConsumed"
sortOrder: Qt.DescendingOrder
}
Connections {
target: engine.thingManager
onFetchingDataChanged: {
var thingIds = []
for (var i = 0; i < consumers.count; i++) {
thingIds.push(consumers.get(i).id)
}
powerLogs.thingIds = thingIds
onFetchingDataChanged: root.update()
}
Connections {
target: engine.tagsManager
onBusyChanged: root.update()
}
function update() {
if (engine.thingManager.fetchingData || engine.tagsManager.busy) {
return
}
powerLogs.loadingInhibited = true
var thingIds = []
for (var i = 0; i < consumers.count; i++) {
thingIds.push(consumers.get(i).id)
}
powerLogs.thingIds = thingIds
var config = root.configs[selectionTabs.currentValue.config]
// print("config:", config.startTime(), config.sampleList(), config.sampleListNames())
powerLogs.sampleRate = config.sampleRate
powerLogs.startTime = config.startTime()
powerLogs.sampleList = config.sampleList()
barSeries.clear();
barSeries.thingBarSetMap = ({})
valueAxis.max = 0
categoryAxis.categories = config.sampleListNames()
chartView.animationOptions = ChartView.SeriesAnimations
powerLogs.loadingInhibited = false
}
ThingPowerLogs {
id: powerLogs
engine: _engine
sampleRate: EnergyLogs.SampleRate1Day
startTime: root.yearStart
loadingInhibited: thingIds.length === 0
loadingInhibited: true
property var sampleList: null
onFetchingDataChanged: {
if (!fetchingData) {
barSeries.clear();
for (var j = 0; j < consumers.count; j++) {
var consumer = consumers.get(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 < daysList.length; i++) {
var start = powerLogs.find(consumer.id, new Date(daysList[i]))
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 < daysList.length -1 ? powerLogs.find(consumer.id, new Date(daysList[i+1])) : null
var endValue = end !== null ? end.totalConsumption : start !== null ? consumer.stateByName("totalEnergyConsumed").value : 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
}
// 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)
}
var barSet = barSeries.append(consumer.name, consumptionValues)
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)
})
}
}
}
}
Item {
id: labelsLayout
x: Style.smallMargins
y: root.plotArea.y
height: root.plotArea.height
width: plotArea.x - x
Repeater {
model: valueAxis.tickCount
delegate: Label {
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
width: parent.width - Style.smallMargins
horizontalAlignment: Text.AlignRight
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1)))).toFixed(0) + "kWh"
verticalAlignment: Text.AlignTop
font: Style.extraSmallFont
color: !powerLogs.fetchingData && powerLogs.count > 0 ? Style.foregroundColor : Style.gray
onEntriesAdded: {
if (fetchingData) {
return
}
chartView.animationOptions = ChartView.NoAnimation
for (var i = 0; i < entries.length; i++) {
var entry = entries[i]
var thing = engine.thingManager.things.getThing(entry.thingId)
// print("Adding new sample. thing:", thing.name);
// print("Timestamp:", entry.timestamp)
var totalEnergyConsumedState = thing.stateByName("totalEnergyConsumed")
// print("current total:", entry.totalConsumption)
var consumptionValue = totalEnergyConsumedState.value - entry.totalConsumption
// print("new slot total:", consumptionValue)
categoryAxis.categories = configs[selectionTabs.currentValue.config].sampleListNames()
barSeries.thingBarSetMap[thing].append(consumptionValue)
barSeries.thingBarSetMap[thing].remove(0, 1);
}
chartView.animationOptions = ChartView.SeriesAnimations
}
}
BarSeries {
id: barSeries
axisX: BarCategoryAxis {
id: categoryAxis
categories: daysListNames
labelsColor: !powerLogs.fetchingData && powerLogs.count > 0 ? Style.foregroundColor : Style.gray
labelsFont: Style.extraSmallFont
gridVisible: false
gridLineColor: Style.tileOverlayColor
lineVisible: false
titleVisible: false
shadesVisible: false
ColumnLayout {
anchors.fill: parent
Label {
Layout.fillWidth: true
Layout.margins: Style.smallMargins
horizontalAlignment: Text.AlignHCenter
text: qsTr("Consumers statistics")
}
axisY: ValueAxis {
id: valueAxis
min: 0
gridLineColor: Style.tileOverlayColor
labelsVisible: false
labelsColor: Style.foregroundColor
labelsFont: Style.extraSmallFont
lineVisible: false
titleVisible: false
shadesVisible: false
function adjustMax(newValue) {
if (max < newValue) {
max = Math.ceil(newValue)
SelectionTabs {
id: selectionTabs
Layout.fillWidth: true
Layout.leftMargin: Style.smallMargins
Layout.rightMargin: Style.smallMargins
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("Minutes"), config: "minutes" })
selectionTabs.currentIndex = 2
}
}
onCurrentValueChanged: {
root.update()
}
}
}
Label {
x: root.plotArea.x
y: root.plotArea.y
width: root.plotArea.width
height: root.plotArea.height
wrapMode: Text.WordWrap
text: qsTr("No data available")
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
visible: !powerLogs.fetchingData && powerLogs.count == 0
ChartView {
id: chartView
Layout.fillWidth: true
Layout.fillHeight: true
// margins.left: 0
margins.right: 0
margins.bottom: 0
margins.top: 0
backgroundColor: "transparent"
legend.alignment: Qt.AlignBottom
legend.font: Style.extraSmallFont
legend.labelColor: Style.foregroundColor
Item {
id: labelsLayout
x: Style.smallMargins
y: chartView.plotArea.y
height: chartView.plotArea.height
width: chartView.plotArea.x - x
Repeater {
model: valueAxis.tickCount
delegate: Label {
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
width: parent.width - Style.smallMargins
horizontalAlignment: Text.AlignRight
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1)))).toFixed(1) + "kWh"
verticalAlignment: Text.AlignTop
font: Style.extraSmallFont
color: Style.foregroundColor
}
}
}
BarSeries {
id: barSeries
axisX: BarCategoryAxis {
id: categoryAxis
labelsColor: Style.foregroundColor
labelsFont: Style.extraSmallFont
gridVisible: false
gridLineColor: Style.tileOverlayColor
lineVisible: false
titleVisible: false
shadesVisible: false
}
axisY: ValueAxis {
id: valueAxis
min: 0
gridLineColor: Style.tileOverlayColor
labelsVisible: false
labelsColor: Style.foregroundColor
labelsFont: Style.extraSmallFont
lineVisible: false
titleVisible: false
shadesVisible: false
function adjustMax(newValue) {
if (max < newValue) {
max = Math.ceil(newValue)
}
}
}
property var thingBarSetMap: ({})
}
}
}
}

View File

@ -10,6 +10,13 @@ Item {
property EnergyManager energyManager: null
property ThingsProxy consumers: ThingsProxy {
engine: _engine
shownInterfaces: ["smartmeterconsumer"]
}
property var colors: null
property int tickCount: 5
property int labelsWidth: 40
@ -148,13 +155,12 @@ Item {
ColorIcon {
anchors.centerIn: parent
name: consumerDelegate.thing ? app.interfacesToIcon(consumerDelegate.thing.thingClass.interfaces) : "energy"
color: root.colors[index % root.colors.length]
}
}
}
}
}
}
}
}

View File

@ -12,6 +12,8 @@ ChartView {
margins.bottom: 0
margins.top: 0
property var colors: null
title: qsTr("Consumers history")
titleColor: Style.foregroundColor
@ -19,6 +21,17 @@ ChartView {
legend.labelColor: Style.foregroundColor
legend.font: Style.extraSmallFont
property ThingsProxy consumers: null
Connections {
target: consumers
onCountChanged: d.updateConsumers()
}
Connections {
target: engine.tagsManager
onBusyChanged: d.updateConsumers()
}
ThingPowerLogs {
id: thingPowerLogs
engine: _engine
@ -75,14 +88,14 @@ ChartView {
}
}
ThingsProxy {
id: consumers
engine: _engine
shownInterfaces: ["smartmeterconsumer"]
}
Connections {
target: engine.thingManager
onFetchingDataChanged: d.updateConsumers()
onThingAdded: {
if (thing.thingClass.interfaces.indexOf("smartmeterconsumer") >= 0) {
d.updateConsumers();
}
}
}
@ -100,9 +113,10 @@ ChartView {
property var thingsSeriesMap: ({})
function updateConsumers() {
if (engine.thingManager.fetchingData) {
if (engine.thingManager.fetchingData || engine.tagsManager.busy) {
return;
}
thingPowerLogs.loadingInhibited = true;
for (var thingId in d.thingsSeriesMap) {
root.removeSeries(d.thingsSeriesMap[thingId])
@ -116,20 +130,22 @@ ChartView {
var baseSeries = zeroSeries;
if (i > 0) {
baseSeries = d.thingsSeriesMap[consumerThingIds[i-1]].upperSeries
print("base for:", thing.name, "is", engine.thingManager.things.getThing(consumerThingIds[i-1]).name)
// print("base for:", thing.name, "is", engine.thingManager.things.getThing(consumerThingIds[i-1]).name)
}
var series = root.createSeries(ChartView.SeriesTypeArea, thing.name, dateTimeAxis, valueAxis)
series.lowerSeries = baseSeries
series.upperSeries = lineSeriesComponent.createObject(series)
series.color = root.colors[i % root.colors.length]
series.borderWidth = 0;
series.borderColor = series.color
print("Adding thingId series", thing.id, thing.name)
// print("Adding thingId series", thing.id, thing.name)
d.thingsSeriesMap[thing.id] = series
consumerThingIds.push(thing.id)
}
thingPowerLogs.thingIds = consumerThingIds;
thingPowerLogs.loadingInhibited = false;
}
}
@ -197,7 +213,7 @@ ChartView {
id: consumptionSeries
axisX: dateTimeAxis
axisY: valueAxis
// color: Style.accentColor
color: Style.gray
borderWidth: 0
borderColor: color
name: qsTr("Unknown")

View File

@ -0,0 +1,84 @@
import QtQuick 2.3
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.3
import Nymea 1.0
import "qrc:/ui/components"
SettingsPageBase {
id: root
title: qsTr("Energy settings")
property EnergyManager energyManager: null
SettingsPageSectionHeader {
text: qsTr("General")
visible: rootMeterProxy.count > 1
}
Label {
Layout.fillWidth: true
Layout.leftMargin: Style.margins
Layout.rightMargin: Style.margins
wrapMode: Text.WordWrap
text: qsTr("Multiple energy meters are installed in the system. Please select the one you'd like to use as the root meter. That is, the one measuring the entire household.")
visible: rootMeterProxy.count > 1
}
RowLayout {
Layout.fillWidth: true
visible: rootMeterProxy.count > 1
Layout.leftMargin: Style.margins
Layout.rightMargin: Style.margins
Label {
text: qsTr("Root meter")
}
ComboBox {
Layout.fillWidth: true
model: ThingsProxy {
id: rootMeterProxy
engine: _engine
shownInterfaces: ["energymeter"]
}
textRole: "name"
currentIndex: rootMeterProxy.indexOf(rootMeterProxy.getThing(energyManager.rootMeterId))
Component.onCompleted: print("root meter id:", energyManager.rootMeterId)
onActivated: {
energyManager.setRootMeterId(rootMeterProxy.get(index).id)
}
}
}
SettingsPageSectionHeader {
text: qsTr("Consumers")
}
Repeater {
model: ThingsProxy {
engine: _engine
shownInterfaces: ["smartmeterconsumer"]
}
delegate: CheckDelegate {
Layout.fillWidth: true
text: model.name
checked: !tagWatcher.tag
onToggled: {
if (checked) {
engine.tagsManager.untagThing(model.id, "hiddenInEnergyView")
} else {
engine.tagsManager.tagThing(model.id, "hiddenInEnergyView", "1")
}
}
TagWatcher {
id: tagWatcher
tags: engine.tagsManager.tags
thingId: model.id
tagId: "hiddenInEnergyView"
}
}
}
}

View File

@ -5,341 +5,234 @@ import QtCharts 2.3
import Nymea 1.0
import "qrc:/ui/components/"
ColumnLayout {
StatsBase {
id: root
property EnergyManager energyManager: null
readonly property date minuteStart: {
var d = new Date();
d.setSeconds(0, 0)
return d;
}
readonly property var minutesList: {
var ret = []
for (var i = 15; i >= 0; i--) {
var last = new Date(minuteStart)
ret.push(last.setTime(last.getTime() - i * 60 * 1000))
}
return ret;
}
readonly property var minutesListNames: {
var ret = []
for (var i = 0; i < minutesList.length; i++) {
ret.push(new Date(minutesList[i]).toLocaleString(Qt.locale(), "hh:mm"))
}
return ret;
QtObject {
id: d
property BarSet consumptionSet: null
property BarSet productionSet: null
property BarSet acquisitionSet: null
property BarSet returnSet: null
}
readonly property date hourStart: {
var d = new Date();
d.setMinutes(0, 0, 0);
return d;
}
readonly property var hoursList: {
var ret = []
for (var i = 24; i >= 0; i--) {
var last = new Date(hourStart)
}
return ret;
}
readonly property var hoursListNames: {
var ret = [];
for (var i = 0; i < hoursList.length; i++) {
ret.push(new Date(hoursList[i]).toLocaleString(Qt.locale(), "dd"));
}
return ret;
}
readonly property date dayStart: {
var d = new Date();
d.setHours(0,0,0,0);
return d;
}
readonly property var daysList: {
var ret = []
for (var i = 6; i >= 0; i--) {
var last = new Date(dayStart)
ret.push(last.setDate(last.getDate() - i))
}
return ret;
}
readonly property var daysListNames: {
var ret = []
for (var i = 0; i < daysList.length; i++) {
ret.push(new Date(daysList[i]).toLocaleString(Qt.locale(), "ddd"))
}
return ret;
}
readonly property date weekStart: {
var d = new Date();
d.setHours(0, 0, 0, 0);
d.setDate(d.getDate() - d.getDay());
return d
}
readonly property date monthStart: {
var d = new Date();
d.setHours(0,0,0,0);
d.setDate(1);
return d;
}
readonly property date yearStart: {
var d = new Date();
d.setHours(0,0,0,0);
d.setDate(1);
d.setMonth(0);
return d;
}
Label {
Layout.fillWidth: true
Layout.margins: Style.smallMargins
horizontalAlignment: Text.AlignHCenter
text: qsTr("Energy consumption statistics")
}
SelectionTabs {
id: selectionTabs
Layout.fillWidth: true
Layout.leftMargin: Style.smallMargins
Layout.rightMargin: Style.smallMargins
model: ListModel {
// ListElement {
// modelData: qsTr("Year")
// sampleRate: EnergyLogs.SampleRate1Year
// }
// ListElement {
// text: qsTr("Month")
// sampleRate: EnergyLogs.SampleRate1Month
// }
// ListElement {
// text: qsTr("Week")
// sampleRate: EnergyLogs.SampleRate1Week
// }
Component.onCompleted: {
append({modelData: qsTr("Day"), config: "days", sampleRate: EnergyLogs.SampleRate1Day, startTime: yearStart, sampleList: daysList, sampleListNames: daysListNames })
append({modelData: qsTr("Hour"), config: "hours", sampleRate: EnergyLogs.SampleRate1Hour, startTime: monthStart, sampleList: hoursList, sampleListNames: hoursListNames })
}
// ListElement {
// modelData: qsTr("Day")
// sampleRate: EnergyLogs.SampleRate1Day
// startTime: weekStart
// }
// ListElement {
// modelData: qsTr("Hour")
// sampleRate: EnergyLogs.SampleRate1Hour
// startTime: dayStart
// }
}
// currentIndex: 3
onCurrentValueChanged: {
print("Selecging model:", currentValue)
powerBalanceLogs.loadingInhibited = true
powerBalanceLogs.sampleRate = currentValue.sampleRate
powerBalanceLogs.startTime = currentValue.startTime
powerBalanceLogs.loadingInhibited = false
consumptionSeries.remove(0, consumptionSeries.count-1)
productionSeries.remove(0, productionSeries.count-1)
acquisitionSeries.remove(0, acquisitionSeries.count-1)
returnSeries.remove(0, returnSeries.count-1)
print("sample list names:", currentValue.sampleListNames)
categoryAxis.categories = currentValue.sampleListNames
}
}
Connections {
target: energyManager
onPowerBalanceChanged: {
var start = powerBalanceLogs.get(powerBalanceLogs.count - 1)
print("updating", start.timestamp, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0))
consumptionSeries.replace(consumptionSeries.count - 1, root.energyManager.totalConsumption - (start ? start.totalConsumption : 0))
productionSeries.replace(productionSeries.count - 1, root.energyManager.totalProduction - (start ? start.totalProduction : 0))
acquisitionSeries.replace(acquisitionSeries.count - 1, root.energyManager.totalAcquisition - (start ? start.totalAcquisition : 0))
returnSeries.replace(returnSeries.count - 1, root.energyManager.totalReturn - (start ? start.totalReturn : 0))
}
}
PowerBalanceLogs {
id: powerBalanceLogs
engine: _engine
// sampleRate: EnergyLogs.SampleRate1Day
// startTime: root.yearStart;
sampleRate: selectionTabs.currentValue.sampleRate// EnergyLogs.SampleRate1Min
startTime: selectionTabs.currentValue.startTime // root.weekStart;
property var sampleList: minutesList
onFetchingDataChanged: {
if (!fetchingData) {
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.timestamp, start.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)
consumptionSeries.append(consumptionValue)
productionSeries.append(productionValue)
acquisitionSeries.append(acquisitionValue)
returnSeries.append(returnValue)
valueAxis.adjustMax(consumptionValue)
valueAxis.adjustMax(productionValue)
valueAxis.adjustMax(acquisitionValue)
valueAxis.adjustMax(returnValue)
}
}
}
onEntryAdded: {
if (fetchingData) {
return
}
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)
consumptionSeries.append(consumptionValue)
productionSeries.append(productionValue)
acquisitionSeries.append(acquisitionValue)
returnSeries.append(returnValue)
consumptionSeries.remove(0, 1);
productionSeries.remove(0, 1);
acquisitionSeries.remove(0, 1);
returnSeries.remove(0, 1);
}
}
ChartView {
id: chartView
Layout.fillWidth: true
Layout.fillHeight: true
backgroundColor: "transparent"
legend.alignment: Qt.AlignBottom
legend.font: Style.extraSmallFont
legend.labelColor: !powerBalanceLogs.fetchingData && powerBalanceLogs.count > 0 ? Style.foregroundColor : Style.gray
// margins.left: 0
margins.right: 0
margins.bottom: 0
margins.top: 0
// title: qsTr("Energy consumption statistics")
// titleColor: "red"// Style.foregroundColor
Item {
id: labelsLayout
x: Style.smallMargins
y: chartView.plotArea.y
height: chartView.plotArea.height
width: chartView.plotArea.x - x
enabled: !powerBalanceLogs.fetchingData && powerBalanceLogs.count > 0
Repeater {
model: valueAxis.tickCount
delegate: Label {
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
width: parent.width - Style.smallMargins
horizontalAlignment: Text.AlignRight
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1)))).toFixed(0) + "kWh"
verticalAlignment: Text.AlignTop
font: Style.extraSmallFont
}
}
}
BarSeries {
axisX: BarCategoryAxis {
id: categoryAxis
// categories: daysListNames
// categories: minutesListNames
labelsColor: !powerBalanceLogs.fetchingData && powerBalanceLogs.count > 0 ? Style.foregroundColor : Style.gray
labelsFont: Style.extraSmallFont
gridVisible: false
gridLineColor: Style.tileOverlayColor
lineVisible: false
titleVisible: false
shadesVisible: false
}
axisY: ValueAxis {
id: valueAxis
min: 0
gridLineColor: Style.tileOverlayColor
labelsVisible: false
labelsColor: Style.foregroundColor
labelsFont: Style.extraSmallFont
lineVisible: false
titleVisible: false
shadesVisible: false
function adjustMax(newValue) {
if (max < newValue) {
max = newValue // Math.ceil(newValue / 100) * 100
}
}
}
BarSet {
id: consumptionSeries
label: qsTr("Consumed")
borderWidth: 0
}
BarSet {
id: productionSeries
label: qsTr("Produced")
color: Style.green
borderWidth: 0
borderColor: color
}
BarSet {
id: acquisitionSeries
label: qsTr("From grid")
color: Style.red
borderWidth: 0
borderColor: color
}
BarSet {
id: returnSeries
label: qsTr("To grid")
color: Style.orange
borderWidth: 0
borderColor: color
}
}
ColumnLayout {
anchors.fill: parent
Label {
x: chartView.plotArea.x
y: chartView.plotArea.y
width: chartView.plotArea.width
height: chartView.plotArea.height
wrapMode: Text.WordWrap
text: qsTr("No data available")
Layout.fillWidth: true
Layout.margins: Style.smallMargins
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
visible: !powerBalanceLogs.fetchingData && powerBalanceLogs.count == 0
text: qsTr("Energy consumption statistics")
}
SelectionTabs {
id: selectionTabs
Layout.fillWidth: true
Layout.leftMargin: Style.smallMargins
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("Minutes"), config: "minutes" })
selectionTabs.currentIndex = 2
}
}
onCurrentValueChanged: {
var config = root.configs[currentValue.config]
print("config:", config.startTime(), config.sampleList(), config.sampleListNames())
powerBalanceLogs.loadingInhibited = true
powerBalanceLogs.sampleRate = config.sampleRate
powerBalanceLogs.startTime = config.startTime()
powerBalanceLogs.sampleList = config.sampleList()
powerBalanceLogs.loadingInhibited = false
barSeries.clear();
d.consumptionSet = barSeries.append(qsTr("Consumed"), [])
d.consumptionSet.color = Style.blue
d.consumptionSet.borderWidth = 0
d.productionSet = barSeries.append(qsTr("Produced"), [])
d.productionSet.color = Style.green
d.productionSet.borderWidth = 0
d.acquisitionSet = barSeries.append(qsTr("From grid"), [])
d.acquisitionSet.color = Style.red
d.acquisitionSet.borderWidth = 0
d.returnSet = barSeries.append(qsTr("To grid"), [])
d.returnSet.color = Style.orange
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))
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))
d.returnSet.replace(d.returnSet.count - 1, root.energyManager.totalReturn - (start ? start.totalReturn : 0))
}
}
PowerBalanceLogs {
id: powerBalanceLogs
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)
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)
}
return;
}
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)
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)
}
}
}
onEntryAdded: {
if (fetchingData) {
return
}
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)
chartView.animationOptions = ChartView.NoAnimation
categoryAxis.categories = configs[selectionTabs.currentValue.config].sampleListNames()
d.consumptionSet.append(consumptionValue)
d.productionSet.append(productionValue)
d.acquisitionSet.append(acquisitionValue)
d.returnSet.append(returnValue)
d.consumptionSet.remove(0, 1);
d.productionSet.remove(0, 1);
d.acquisitionSet.remove(0, 1);
d.returnSet.remove(0, 1);
chartView.animationOptions = ChartView.SeriesAnimations
}
}
ChartView {
id: chartView
Layout.fillWidth: true
Layout.fillHeight: true
animationOptions: ChartView.NoAnimations
backgroundColor: "transparent"
legend.alignment: Qt.AlignBottom
legend.font: Style.extraSmallFont
legend.labelColor: Style.foregroundColor
// margins.left: 0
margins.right: 0
margins.bottom: 0
margins.top: 0
Item {
id: labelsLayout
x: Style.smallMargins
y: chartView.plotArea.y
height: chartView.plotArea.height
width: chartView.plotArea.x - x
Repeater {
model: valueAxis.tickCount
delegate: Label {
y: parent.height / (valueAxis.tickCount - 1) * index - font.pixelSize / 2
width: parent.width - Style.smallMargins
horizontalAlignment: Text.AlignRight
text: ((valueAxis.max - (index * valueAxis.max / (valueAxis.tickCount - 1)))).toFixed(1) + "kWh"
verticalAlignment: Text.AlignTop
font: Style.extraSmallFont
}
}
}
BarSeries {
id: barSeries
axisX: BarCategoryAxis {
id: categoryAxis
labelsColor: Style.foregroundColor
labelsFont: Style.extraSmallFont
gridVisible: false
gridLineColor: Style.tileOverlayColor
lineVisible: false
titleVisible: false
shadesVisible: false
}
axisY: ValueAxis {
id: valueAxis
min: 0
gridLineColor: Style.tileOverlayColor
labelsVisible: false
labelsColor: Style.foregroundColor
labelsFont: Style.extraSmallFont
lineVisible: false
titleVisible: false
shadesVisible: false
function adjustMax(newValue) {
if (max < newValue) {
max = newValue // Math.ceil(newValue / 100) * 100
}
}
}
}
}
}
}

View File

@ -0,0 +1,177 @@
import QtQuick 2.0
import Nymea 1.0
Item {
id: root
property int minutesCount: 10
property int hoursCount: 12
property int daysCount: 7
property int weeksCount: 12
property int monthsCount: 12
property var configs: ({
minutes: {
startTime: minutesStart,
sampleRate: EnergyLogs.SampleRate1Min,
sampleList: minutesList,
sampleListNames: minutesListNames
},
hours: {
startTime: hoursStart,
sampleRate: EnergyLogs.SampleRate1Hour,
sampleList: hoursList,
sampleListNames: hoursListNames
},
days: {
startTime: daysStart,
sampleRate: EnergyLogs.SampleRate1Day,
sampleList: daysList,
sampleListNames: daysListNames
},
weeks: {
startTime: weeksStart,
sampleRate: EnergyLogs.SampleRate1Week,
sampleList: weeksList,
sampleListNames: weeksListNames
},
months: {
startTime: monthsStart,
sampleRate: EnergyLogs.SampleRate1Month,
sampleList: monthsList,
sampleListNames: monthsListNames
}
})
function minutesStart() {
var d = new Date();
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 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 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 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 daysStart() {
var d = new Date();
d.setHours(0,0,0,0);
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 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 weeksStart() {
var d = new Date();
d.setHours(0, 0, 0, 0);
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 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 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 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 yearStart() {
var d = new Date();
d.setHours(0,0,0,0);
d.setDate(1);
d.setMonth(0);
return d;
}
}

View File

@ -40,7 +40,7 @@ SettingsPageBase {
title: qsTr("All packages")
property Packages packages: engine.systemController.packages
property string filter: ""
property alias filter: filterTextField.text
ColumnLayout {
Layout.fillWidth: true
@ -53,8 +53,6 @@ SettingsPageBase {
TextField {
id: filterTextField
Layout.fillWidth: true
text: packageListPage.filter
onTextChanged: packageListPage.filter = text
}
ColorIcon {
name: "close"
@ -78,7 +76,7 @@ SettingsPageBase {
model: PackagesFilterModel {
id: filterModel
packages: packageListPage.packages
nameFilter: packageListPage.filter
nameFilter: filterTextField.displayText
}
delegate: NymeaSwipeDelegate {