From 9717825c16edec69d2936564452217e42bd3296f Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 27 Jun 2023 17:05:05 +0200 Subject: [PATCH] Add support for localized and dynamic possibleValues of states --- libnymea-app/thingmanager.cpp | 19 ++++++++-- libnymea-app/types/state.cpp | 13 +++++++ libnymea-app/types/state.h | 7 +++- libnymea-app/types/statetype.cpp | 21 ++++++++--- libnymea-app/types/statetype.h | 13 ++++--- nymea-app/ui/delegates/ParamDelegate.qml | 2 ++ .../statedelegates/ComboBoxDelegate.qml | 31 +++++++++++++--- .../SmartMeterDeviceListPage.qml | 5 ++- nymea-app/ui/devicepages/GenericThingPage.qml | 35 ++++++++++++++++--- .../ui/devicepages/SmartMeterDevicePage.qml | 12 ++++--- 10 files changed, 132 insertions(+), 26 deletions(-) diff --git a/libnymea-app/thingmanager.cpp b/libnymea-app/thingmanager.cpp index f4b739a9..9854e2f3 100644 --- a/libnymea-app/thingmanager.cpp +++ b/libnymea-app/thingmanager.cpp @@ -129,7 +129,6 @@ void ThingManager::notificationReceived(const QVariantMap &data) } QUuid stateTypeId = params.value("stateTypeId").toUuid(); QVariant value = params.value("value"); -// qDebug() << "Thing state changed for:" << dev->name() << "State name:" << dev->thingClass()->stateTypes()->getStateType(stateTypeId) << "value:" << value; State *state = thing->state(stateTypeId); state->setValue(value); if (params.contains("minValue")) { @@ -138,6 +137,9 @@ void ThingManager::notificationReceived(const QVariantMap &data) if (params.contains("maxValue")) { state->setMaxValue(params.value("maxValue")); } + if (params.contains("possibleValues")) { + state->setPossibleValues(params.value("possibleValues").toList()); + } emit thingStateChanged(thing->id(), stateTypeId, value); } else if (notification == "Integrations.ThingAdded") { Thing *thing = unpackThing(this, data.value("params").toMap().value("thing").toMap(), m_thingClasses); @@ -851,7 +853,14 @@ StateType *ThingManager::unpackStateType(const QVariantMap &stateTypeMap, QObjec stateType->setDisplayName(stateTypeMap.value("displayName").toString()); stateType->setIndex(stateTypeMap.value("index").toInt()); stateType->setDefaultValue(stateTypeMap.value("defaultValue")); - stateType->setAllowedValues(stateTypeMap.value("possibleValues").toList()); + QVariantList possibleValuesList = stateTypeMap.value("possibleValues").toList(); + QVariantList possibleValuesNamesList = stateTypeMap.value("possibleValuesDisplayNames").toList(); + QStringList possibleValuesDisplayNames; + for (int i = 0; i < possibleValuesList.count(); i++) { + QVariant possibleValue = possibleValuesList.at(i); + possibleValuesDisplayNames.append(possibleValuesNamesList.count() > i ? possibleValuesNamesList.at(i).toString() : possibleValue.toString()); + } + stateType->setPossibleValues(stateTypeMap.value("possibleValues").toList(), possibleValuesDisplayNames); stateType->setType(stateTypeMap.value("type").toString()); stateType->setMinValue(stateTypeMap.value("minValue")); stateType->setMaxValue(stateTypeMap.value("maxValue")); @@ -861,6 +870,7 @@ StateType *ThingManager::unpackStateType(const QVariantMap &stateTypeMap, QObjec Types::IOType ioType = static_cast(metaEnum.keyToValue(stateTypeMap.value("ioType").toByteArray())); stateType->setIOType(ioType); + qCritical() << "*** possible vals" << stateType->name() << possibleValuesDisplayNames << stateTypeMap.value("possibleValuesDisplayNames").toList(); return stateType; } @@ -981,6 +991,11 @@ Thing* ThingManager::unpackThing(ThingManager *thingManager, const QVariantMap & } else { state->setMaxValue(stateType->maxValue()); } + if (stateMap.contains("possibleValues")) { + state->setPossibleValues(stateMap.value("possibleValues").toList()); + } else { + state->setPossibleValues(stateType->possibleValues()); + } } thing->setStates(states); diff --git a/libnymea-app/types/state.cpp b/libnymea-app/types/state.cpp index ecfa6ba6..69c97e64 100644 --- a/libnymea-app/types/state.cpp +++ b/libnymea-app/types/state.cpp @@ -87,3 +87,16 @@ void State::setMaxValue(const QVariant &maxValue) emit maxValueChanged(); } } + +QVariantList State::possibleValues() const +{ + return m_possibleValues; +} + +void State::setPossibleValues(const QVariantList &possibleValues) +{ + if (m_possibleValues != possibleValues) { + m_possibleValues = possibleValues; + emit possibleValuesChanged(); + } +} diff --git a/libnymea-app/types/state.h b/libnymea-app/types/state.h index 1b7913c3..ba8cbeb4 100644 --- a/libnymea-app/types/state.h +++ b/libnymea-app/types/state.h @@ -43,6 +43,7 @@ class State : public QObject Q_PROPERTY(QVariant value READ value NOTIFY valueChanged) Q_PROPERTY(QVariant minValue READ minValue NOTIFY minValueChanged) Q_PROPERTY(QVariant maxValue READ maxValue NOTIFY maxValueChanged) + Q_PROPERTY(QVariantList possibleValues READ possibleValues NOTIFY possibleValuesChanged) public: explicit State(const QUuid &thingId, const QUuid &stateTypeId, const QVariant &value, QObject *parent = nullptr); @@ -59,18 +60,22 @@ public: QVariant maxValue() const; void setMaxValue(const QVariant &maxValue); + QVariantList possibleValues() const; + void setPossibleValues(const QVariantList &possibleValues); + private: QUuid m_thingId; QUuid m_stateTypeId; QVariant m_value; QVariant m_minValue; QVariant m_maxValue; + QVariantList m_possibleValues; signals: void valueChanged(); void minValueChanged(); void maxValueChanged(); - + void possibleValuesChanged(); }; #endif // STATE_H diff --git a/libnymea-app/types/statetype.cpp b/libnymea-app/types/statetype.cpp index 973763f8..149d733a 100644 --- a/libnymea-app/types/statetype.cpp +++ b/libnymea-app/types/statetype.cpp @@ -100,14 +100,27 @@ void StateType::setDefaultValue(const QVariant &defaultValue) m_defaultValue = defaultValue; } -QVariantList StateType::allowedValues() const +QVariantList StateType::possibleValues() const { - return m_allowedValues; + return m_possibleValues; } -void StateType::setAllowedValues(const QVariantList &allowedValues) +void StateType::setPossibleValues(const QVariantList &values, const QStringList &displayNames) { - m_allowedValues = allowedValues; + Q_ASSERT_X(values.count() == displayNames.count(), "StateType", "Display names list length does not match values list length"); + m_possibleValues = values; + m_possibleValuesDisplayNames = displayNames; +} + +QStringList StateType::possibleValuesDisplayNames() const +{ + return m_possibleValuesDisplayNames; +} + +QString StateType::localizedValue(const QVariant &value) const +{ + int idx = m_possibleValues.indexOf(value); + return m_possibleValuesDisplayNames.at(idx); } Types::Unit StateType::unit() const diff --git a/libnymea-app/types/statetype.h b/libnymea-app/types/statetype.h index 930fd6e3..127adfb6 100644 --- a/libnymea-app/types/statetype.h +++ b/libnymea-app/types/statetype.h @@ -46,7 +46,9 @@ class StateType : public QObject Q_PROPERTY(QString type READ type CONSTANT) Q_PROPERTY(int index READ index CONSTANT) Q_PROPERTY(QVariant defaultValue READ defaultValue CONSTANT) - Q_PROPERTY(QVariantList allowedValues READ allowedValues CONSTANT) + Q_PROPERTY(QVariantList allowedValues READ possibleValues CONSTANT) // Deprecated + Q_PROPERTY(QVariantList possibleValues READ possibleValues CONSTANT) + Q_PROPERTY(QStringList possibleValuesDisplayNames READ possibleValuesDisplayNames CONSTANT) Q_PROPERTY(Types::Unit unit READ unit CONSTANT) Q_PROPERTY(Types::IOType ioType READ ioType CONSTANT) Q_PROPERTY(QVariant minValue READ minValue CONSTANT) @@ -74,8 +76,10 @@ public: QVariant defaultValue() const; void setDefaultValue(const QVariant &defaultValue); - QVariantList allowedValues() const; - void setAllowedValues(const QVariantList &allowedValues); + QVariantList possibleValues() const; + QStringList possibleValuesDisplayNames() const; + void setPossibleValues(const QVariantList &values, const QStringList &displayNames); + Q_INVOKABLE QString localizedValue(const QVariant &value) const; Types::Unit unit() const; void setUnit(const Types::Unit &unit); @@ -96,7 +100,8 @@ private: QString m_type; int m_index; QVariant m_defaultValue; - QVariantList m_allowedValues; + QVariantList m_possibleValues; + QStringList m_possibleValuesDisplayNames; Types::Unit m_unit = Types::UnitNone; Types::IOType m_ioType = Types::IOTypeNone; QVariant m_minValue; diff --git a/nymea-app/ui/delegates/ParamDelegate.qml b/nymea-app/ui/delegates/ParamDelegate.qml index 5953eaff..87933447 100644 --- a/nymea-app/ui/delegates/ParamDelegate.qml +++ b/nymea-app/ui/delegates/ParamDelegate.qml @@ -54,6 +54,8 @@ ItemDelegate { contentItem: ColumnLayout { id: contentItemColumn anchors.fill: parent + anchors.leftMargin: Style.margins + anchors.rightMargin: Style.margins RowLayout { Layout.fillWidth: true spacing: Style.margins diff --git a/nymea-app/ui/delegates/statedelegates/ComboBoxDelegate.qml b/nymea-app/ui/delegates/statedelegates/ComboBoxDelegate.qml index 7b057481..2ed2c5f9 100644 --- a/nymea-app/ui/delegates/statedelegates/ComboBoxDelegate.qml +++ b/nymea-app/ui/delegates/statedelegates/ComboBoxDelegate.qml @@ -36,12 +36,35 @@ import Nymea 1.0 import "../../components" ComboBox { + id: root property var value property var possibleValues + property var possibleValuesDisplayNames signal changed(var value) - model: possibleValues - currentIndex: possibleValues.indexOf(value) - onActivated: changed(model[index]) - Component.onCompleted: print("completed. values", possibleValues, "value", value) + + function update() { + print("possible:", root.possibleValues, root.possibleValuesDisplayNames) + listModel.clear(); + for (var i = 0; i < root.possibleValues.length; i++) { + var value = root.possibleValues[i] + var label = root.possibleValuesDisplayNames.length > i ? root.possibleValuesDisplayNames[i] : root.possibleValues[i] + listModel.append({value: value, label: label}) + } + currentIndex = possibleValues.indexOf(root.value) + } + + model: ListModel { + id: listModel + } + onActivated: changed(model.get(index).value) + textRole: "label" + Component.onCompleted: { + print("completed. values", possibleValues, "value", root.value) + update(); + } + onPossibleValuesChanged: { + print("Possible values changed:", possibleValues) + update(); + } } diff --git a/nymea-app/ui/devicelistpages/SmartMeterDeviceListPage.qml b/nymea-app/ui/devicelistpages/SmartMeterDeviceListPage.qml index e4fc31fc..ae7fce80 100644 --- a/nymea-app/ui/devicelistpages/SmartMeterDeviceListPage.qml +++ b/nymea-app/ui/devicelistpages/SmartMeterDeviceListPage.qml @@ -79,6 +79,7 @@ ThingsListPageBase { property bool isConsumer: itemDelegate.thing.thingClass.interfaces.indexOf("smartmeterconsumer") >= 0 property bool isProduction: currentPowerState.value < 0 property bool isConsumption: currentPowerState.value > 0 + property bool isIdling: currentPowerState.value === 0 property double absValue: Math.abs(currentPowerState.value) property double cleanVale: (absValue / (absValue > 1000 ? 1000 : 1)).toFixed(1) property string unit: absValue > 1000 ? "kW" : "W" @@ -105,9 +106,11 @@ ThingsListPageBase { if (dataGrid.isProduction) { //: e.g. Discharging at 5kW return qsTr("Discharging at %1").arg(dataGrid.cleanVale + " " + dataGrid.unit) - } else { + } else if (dataGrid.isConsumption){ //: e.g. Charging at 5kW return qsTr("Charging at %1").arg(dataGrid.cleanVale + " " + dataGrid.unit) + } else { + return qsTr("Idling") } } else if (dataGrid.isProducer && !dataGrid.isConsumer) { diff --git a/nymea-app/ui/devicepages/GenericThingPage.qml b/nymea-app/ui/devicepages/GenericThingPage.qml index bcd31dc5..6a16d699 100644 --- a/nymea-app/ui/devicepages/GenericThingPage.qml +++ b/nymea-app/ui/devicepages/GenericThingPage.qml @@ -199,7 +199,7 @@ ThingPageBase { switch (stateDelegate.stateType.type.toLowerCase()) { case "string": if (isWritable) { - if (stateDelegate.stateType.allowedValues.length > 0) { + if (stateDelegate.stateType.possibleValues.length > 0) { sourceComp = "ComboBoxDelegate.qml" } else { sourceComp = "TextFieldDelegate.qml"; @@ -252,12 +252,12 @@ ThingPageBase { var maxValue = stateDelegate.stateType.maxValue !== undefined ? stateDelegate.stateType.maxValue : 2000000000; - print(stateDelegate.stateType.minValue) - print("pushing delegate for", stateDelegate.stateType.name, "from:", minValue, "to:", maxValue) + print("pushing delegate for", stateDelegate.stateType.name, "from:", minValue, "to:", maxValue, "possible:", stateDelegate.stateType.possibleValuesDisplayNames) stateDelegateLoader.setSource("../delegates/statedelegates/" + sourceComp, { -// value: root.thing.states.getState(stateType.id).value, - possibleValues: stateDelegate.stateType.allowedValues, + value: root.thing.states.getState(stateType.id).value, + possibleValues: stateDelegate.stateType.possibleValues, + possibleValuesDisplayNames: stateDelegate.stateType.possibleValuesDisplayNames, from: minValue, to: maxValue, unit: stateDelegate.stateType.unit, @@ -306,6 +306,30 @@ ThingPageBase { property: "to" value: stateDelegate.thingState.maxValue } + Binding { + target: stateDelegateLoader.item.hasOwnProperty("possibleValues") ? stateDelegateLoader.item : null + property: "possibleValues" + value: stateDelegate.thingState.possibleValues + } + Binding { + target: stateDelegateLoader.item.hasOwnProperty("possibleValuesDisplayNames") ? stateDelegateLoader.item : null + property: "possibleValuesDisplayNames" + value: { + print("updating displayNames", stateDelegate.thingState.possibleValues) + var ret = [] + for (var i = 0; i < stateDelegate.thingState.possibleValues.length; i++) { + var possibleValue = stateDelegate.thingState.possibleValues[i] + var idx = stateDelegate.stateType.possibleValues.indexOf(possibleValue) + print("value:", possibleValue, idx) + if (idx >= 0) { + ret.push(stateDelegate.stateType.possibleValuesDisplayNames[idx]) + } else { + ret.push(possibleValue) + } + } + return ret + } + } Binding { target: stateDelegateLoader.item.hasOwnProperty("unit") ? stateDelegateLoader.item : null property: "unit" @@ -315,6 +339,7 @@ ThingPageBase { Connections { target: stateDelegateLoader.item && stateDelegateLoader.item.hasOwnProperty("changed") ? stateDelegateLoader.item : null onChanged: { + print("Value changed:", value) stateDelegate.enqueueSetValue(value) } } diff --git a/nymea-app/ui/devicepages/SmartMeterDevicePage.qml b/nymea-app/ui/devicepages/SmartMeterDevicePage.qml index f4e0d51b..6ee5a7d1 100644 --- a/nymea-app/ui/devicepages/SmartMeterDevicePage.qml +++ b/nymea-app/ui/devicepages/SmartMeterDevicePage.qml @@ -240,11 +240,13 @@ ThingPageBase { ? qsTr("Total production: %1 kWh").arg('' + root.totalEnergyProducedState.value.toFixed(2) + "") : root.isEnergyMeter ? qsTr("Total acquisition: %1 kWh").arg('' + root.totalEnergyConsumedState.value.toFixed(2) + "") + "
" + qsTr("Total return: %1 kWh").arg('' + root.totalEnergyProducedState.value.toFixed(2) + "") - : root.isBattery && isCharging - ? qsTr("At the current rate, the battery will be fully charged at %1.").arg('' + endTime.toLocaleTimeString(Locale.ShortFormat) + "") - : root.isBattery && isDischarging - ? qsTr("At the current rate, the battery will last until %1.").arg('' + endTime.toLocaleTimeString(Locale.ShortFormat) + "") - : "" + : root.isBattery && root.batteryLevelState.value === 0 + ? qsTr("The battery is empty") + : root.isBattery && root.isCharging + ? qsTr("At the current rate, the battery will be fully charged at %1.").arg('' + endTime.toLocaleTimeString(Locale.ShortFormat) + "") + : root.isBattery && root.isDischarging + ? qsTr("At the current rate, the battery will last until %1.").arg('' + endTime.toLocaleTimeString(Locale.ShortFormat) + "") + : "" } } }