Add support for localized and dynamic possibleValues of states

This commit is contained in:
Michael Zanetti 2023-06-27 17:05:05 +02:00
parent 8a8083c2a6
commit 9717825c16
10 changed files with 132 additions and 26 deletions

View File

@ -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<Types::IOType>(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);

View File

@ -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();
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}
}

View File

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

View File

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

View File

@ -240,11 +240,13 @@ ThingPageBase {
? qsTr("Total production: %1 kWh").arg('<span style="font-size:' + Style.bigFont.pixelSize + 'px">' + root.totalEnergyProducedState.value.toFixed(2) + "</span>")
: root.isEnergyMeter
? qsTr("Total acquisition: %1 kWh").arg('<span style="font-size:' + Style.bigFont.pixelSize + 'px">' + root.totalEnergyConsumedState.value.toFixed(2) + "</span>") + "<br>" + qsTr("Total return: %1 kWh").arg('<span style="font-size:' + Style.bigFont.pixelSize + 'px">' + root.totalEnergyProducedState.value.toFixed(2) + "</span>")
: root.isBattery && isCharging
? qsTr("At the current rate, the battery will be fully charged at %1.").arg('<span style="font-size:' + Style.bigFont.pixelSize + 'px">' + endTime.toLocaleTimeString(Locale.ShortFormat) + "</span>")
: root.isBattery && isDischarging
? qsTr("At the current rate, the battery will last until %1.").arg('<span style="font-size:' + Style.bigFont.pixelSize + 'px">' + endTime.toLocaleTimeString(Locale.ShortFormat) + "</span>")
: ""
: 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('<span style="font-size:' + Style.bigFont.pixelSize + 'px">' + endTime.toLocaleTimeString(Locale.ShortFormat) + "</span>")
: root.isBattery && root.isDischarging
? qsTr("At the current rate, the battery will last until %1.").arg('<span style="font-size:' + Style.bigFont.pixelSize + 'px">' + endTime.toLocaleTimeString(Locale.ShortFormat) + "</span>")
: ""
}
}
}