Merge PR #314: Fix conversion of values

This commit is contained in:
Jenkins nymea 2020-08-14 13:09:34 +02:00
commit 768b6a877e
13 changed files with 207 additions and 167 deletions

View File

@ -185,10 +185,10 @@ Thing::ThingError ThingManagerImplementation::setPluginConfig(const PluginId &pl
return Thing::ThingErrorPluginNotFound;
}
ParamList params = buildParams(plugin->configurationDescription(), pluginConfig);
Thing::ThingError verify = ThingUtils::verifyParams(plugin->configurationDescription(), params);
Thing::ThingError verify = ThingUtils::verifyParams(plugin->configurationDescription(), pluginConfig);
if (verify != Thing::ThingErrorNoError)
return verify;
ParamList params = buildParams(plugin->configurationDescription(), pluginConfig);
Thing::ThingError result = plugin->setConfiguration(params);
if (result != Thing::ThingErrorNoError)
@ -199,10 +199,7 @@ Thing::ThingError ThingManagerImplementation::setPluginConfig(const PluginId &pl
settings.beginGroup(plugin->pluginId().toString());
foreach (const Param &param, pluginConfig) {
settings.beginGroup(param.paramTypeId().toString());
settings.setValue("type", static_cast<int>(param.value().type()));
settings.setValue("value", param.value());
settings.endGroup();
settings.setValue(param.paramTypeId().toString(), param.value());
}
settings.endGroup();
@ -259,14 +256,14 @@ ThingDiscoveryInfo* ThingManagerImplementation::discoverThings(const ThingClassI
return discoveryInfo;
}
ParamList effectiveParams = buildParams(thingClass.discoveryParamTypes(), params);
Thing::ThingError result = ThingUtils::verifyParams(thingClass.discoveryParamTypes(), effectiveParams);
Thing::ThingError result = ThingUtils::verifyParams(thingClass.discoveryParamTypes(), params);
if (result != Thing::ThingErrorNoError) {
qCWarning(dcThingManager) << "Thing discovery failed. Parameter verification failed.";
ThingDiscoveryInfo *discoveryInfo = new ThingDiscoveryInfo(thingClassId, params, this);
discoveryInfo->finish(result);
return discoveryInfo;
}
ParamList effectiveParams = buildParams(thingClass.discoveryParamTypes(), params);
ThingDiscoveryInfo *discoveryInfo = new ThingDiscoveryInfo(thingClassId, effectiveParams, this, 30000);
connect(discoveryInfo, &ThingDiscoveryInfo::finished, this, [this, discoveryInfo](){
@ -396,14 +393,14 @@ ThingSetupInfo *ThingManagerImplementation::reconfigureThingInternal(Thing *thin
return info;
}
ParamList finalParams = buildParams(thing->thingClass().paramTypes(), params);
Thing::ThingError result = ThingUtils::verifyParams(thing->thingClass().paramTypes(), finalParams);
Thing::ThingError result = ThingUtils::verifyParams(thing->thingClass().paramTypes(), params);
if (result != Thing::ThingErrorNoError) {
qCWarning(dcThingManager()) << "Cannot reconfigure thing. Params failed validation.";
ThingSetupInfo *info = new ThingSetupInfo(nullptr, this);
info->finish(result);
return info;
}
ParamList finalParams = buildParams(thing->thingClass().paramTypes(), params);
// first remove the thing in the plugin
plugin->thingRemoved(thing);
@ -463,13 +460,13 @@ Thing::ThingError ThingManagerImplementation::setThingSettings(const ThingId &th
qCWarning(dcThingManager()) << "Cannot set thing settings. Thing" << thingId.toString() << "not found";
return Thing::ThingErrorThingNotFound;
}
// build a list of settings using: a) provided new settings b) previous settings and c) default values
ParamList effectiveSettings = buildParams(thing->thingClass().settingsTypes(), settings, thing->settings());
Thing::ThingError status = ThingUtils::verifyParams(thing->thingClass().settingsTypes(), effectiveSettings);
Thing::ThingError status = ThingUtils::verifyParams(thing->thingClass().settingsTypes(), settings);
if (status != Thing::ThingErrorNoError) {
qCWarning(dcThingManager()) << "Error setting thing settings for" << thing->name() << thing->id().toString();
return status;
}
// build a list of settings using: a) provided new settings b) previous settings and c) default values
ParamList effectiveSettings = buildParams(thing->thingClass().settingsTypes(), settings, thing->settings());
thing->setSettings(effectiveSettings);
return Thing::ThingErrorNoError;
}
@ -693,8 +690,7 @@ ThingSetupInfo* ThingManagerImplementation::addConfiguredThingInternal(const Thi
}
// set params
ParamList effectiveParams = buildParams(thingClass.paramTypes(), params);
Thing::ThingError paramsResult = ThingUtils::verifyParams(thingClass.paramTypes(), effectiveParams);
Thing::ThingError paramsResult = ThingUtils::verifyParams(thingClass.paramTypes(), params);
if (paramsResult != Thing::ThingErrorNoError) {
qCWarning(dcThingManager()) << "Cannot add thing. Parameter verification failed.";
ThingSetupInfo *info = new ThingSetupInfo(nullptr, this);
@ -702,6 +698,7 @@ ThingSetupInfo* ThingManagerImplementation::addConfiguredThingInternal(const Thi
return info;
}
ParamList effectiveParams = buildParams(thingClass.paramTypes(), params);
Thing *thing = new Thing(plugin->pluginId(), thingClass, thingId, this);
thing->setParentId(parentId);
if (name.isEmpty()) {
@ -1211,14 +1208,14 @@ ThingActionInfo *ThingManagerImplementation::executeAction(const Action &action)
return info;
}
ParamList finalParams = buildParams(actionType.paramTypes(), action.params());
Thing::ThingError paramCheck = ThingUtils::verifyParams(actionType.paramTypes(), finalParams);
Thing::ThingError paramCheck = ThingUtils::verifyParams(actionType.paramTypes(), action.params());
if (paramCheck != Thing::ThingErrorNoError) {
qCWarning(dcThingManager()) << "Cannot execute action. Parameter verification failed.";
ThingActionInfo *info = new ThingActionInfo(thing, action, this);
info->finish(paramCheck);
return info;
}
ParamList finalParams = buildParams(actionType.paramTypes(), action.params());
finalAction.setParams(finalParams);
ThingActionInfo *info = new ThingActionInfo(thing, finalAction, this, 30000);
@ -1375,42 +1372,25 @@ void ThingManagerImplementation::loadPlugin(IntegrationPlugin *pluginIface, cons
NymeaSettings settings(NymeaSettings::SettingsRolePlugins);
settings.beginGroup("PluginConfig");
ParamList params;
if (settings.childGroups().contains(pluginIface->pluginId().toString())) {
settings.beginGroup(pluginIface->pluginId().toString());
if (!settings.childGroups().isEmpty()) {
// Note: since nymea 0.12.2 the param type gets saved too for better data converting
foreach (const QString &paramTypeIdString, settings.childGroups()) {
ParamTypeId paramTypeId(paramTypeIdString);
ParamType paramType = pluginIface->configurationDescription().findById(paramTypeId);
if (!paramType.isValid()) {
qCWarning(dcThingManager()) << "Not loading Param for plugin" << pluginIface->pluginName() << "because the ParamType for the saved Param" << ParamTypeId(paramTypeIdString).toString() << "could not be found.";
continue;
}
QVariant paramValue;
settings.beginGroup(paramTypeIdString);
paramValue = settings.value("value", paramType.defaultValue());
paramValue.convert(settings.value("type").toInt());
params.append(Param(paramTypeId, paramValue));
settings.endGroup();
}
} else {
// Note: < nymea 0.12.2
foreach (const QString &paramTypeIdString, settings.allKeys()) {
params.append(Param(ParamTypeId(paramTypeIdString), settings.value(paramTypeIdString)));
}
}
settings.endGroup();
} else if (!pluginIface->configurationDescription().isEmpty()){
// plugin requires config but none stored. Init with defaults
foreach (const ParamType &paramType, pluginIface->configurationDescription()) {
Param param(paramType.id(), paramType.defaultValue());
params.append(param);
settings.beginGroup(pluginIface->pluginId().toString());
foreach (const ParamType &paramType, pluginIface->configurationDescription()) {
QVariant value = paramType.defaultValue();
if (settings.contains(paramType.id().toString())) {
value = settings.value(paramType.id().toString());
} else if (settings.childGroups().contains(paramType.id().toString())) {
// 0.12.2 - 0.22 used to store it in subgroups
settings.beginGroup(paramType.id().toString());
value = settings.value("value");
settings.endGroup();
}
value.convert(paramType.type());
Param param(paramType.id(), value);
params.append(param);
}
settings.endGroup();
settings.endGroup(); // pluginId
settings.endGroup(); // PluginConfig
if (params.count() > 0) {
Thing::ThingError status = pluginIface->setConfiguration(params);
@ -1491,69 +1471,70 @@ void ThingManagerImplementation::loadConfiguredThings()
ParamList params;
settings.beginGroup("Params");
if (!settings.childGroups().isEmpty()) {
foreach (const QString &paramTypeIdString, settings.childGroups()) {
ParamTypeId paramTypeId(paramTypeIdString);
ParamType paramType = thingClass.paramTypes().findById(paramTypeId);
QVariant defaultValue;
if (!paramType.isValid()) {
// NOTE: We're not skipping unknown parameters to give plugins a chance to still access old values if they change their config and migrate things over.
qCWarning(dcThingManager()) << "Unknown param" << paramTypeIdString << "for" << thing << ". ParamType could not be found in device class.";
}
// Note: since nymea 0.12.2
QVariant paramValue;
settings.beginGroup(paramTypeIdString);
paramValue = settings.value("value", paramType.defaultValue());
paramValue.convert(settings.value("type").toInt());
params.append(Param(paramTypeId, paramValue));
settings.endGroup(); // ParamId
}
} else {
foreach (const QString &paramTypeIdString, settings.allKeys()) {
params.append(Param(ParamTypeId(paramTypeIdString), settings.value(paramTypeIdString)));
}
}
// Make sure all params are around. if they aren't initialize with default values
foreach (const ParamType &paramType, thingClass.paramTypes()) {
if (!params.hasParam(paramType.id())) {
params.append(Param(paramType.id(), paramType.defaultValue()));
QVariant value = paramType.defaultValue();
if (settings.contains(paramType.id().toString())) {
value = settings.value(paramType.id().toString());
} else if (settings.childGroups().contains(paramType.id().toString())) {
// 0.12.2 - 0.22 used to store in subgroups
settings.beginGroup(paramType.id().toString());
value = settings.value("value");
settings.endGroup(); // paramTypeId
}
value.convert(paramType.type());
Param param(paramType.id(), value);
params.append(param);
}
// In order to give plugins a chance to migrate stuff stored in the params (to e.g. pluginStorage()) we'll load
// params that might have disappeared from the ParamTypes but still have stuff stored in the config
foreach (const QString paramTypeIdString, settings.childKeys()) {
ParamTypeId paramTypeId(paramTypeIdString);
if (!params.hasParam(paramTypeId)) {
qCDebug(dcThingManager()) << "Loading legacy param" << paramTypeIdString << "for thing" << thing->name();
Param param(paramTypeId, settings.value(paramTypeIdString));
params.append(param);
}
}
thing->setParams(params);
// 0.12.2 - 0.22 used to store in subgroups
foreach (const QString &paramTypeIdString, settings.childGroups()) {
settings.beginGroup(paramTypeIdString);
ParamTypeId paramTypeId(paramTypeIdString);
if (!params.hasParam(paramTypeId)) {
qCDebug(dcThingManager()) << "Loading legacy param" << paramTypeIdString << "for thing" << thing->name();
Param param(paramTypeId, settings.value("value"));
params.append(param);
}
settings.endGroup(); // paramTypeId
}
settings.endGroup(); // Params
thing->setParams(params);
ParamList thingSettings;
settings.beginGroup("Settings");
if (!settings.childGroups().isEmpty()) {
foreach (const QString &paramTypeIdString, settings.childGroups()) {
ParamTypeId paramTypeId(paramTypeIdString);
ParamType paramType = thingClass.settingsTypes().findById(paramTypeId);
if (!paramType.isValid()) {
qCWarning(dcThingManager()) << "Not loading Setting for thing" << thing << "because the ParamType for the saved Setting" << ParamTypeId(paramTypeIdString).toString() << "could not be found.";
continue;
}
// Note: since nymea 0.12.2
QVariant paramValue;
settings.beginGroup(paramTypeIdString);
paramValue = settings.value("value", paramType.defaultValue());
paramValue.convert(settings.value("type").toInt());
thingSettings.append(Param(paramTypeId, paramValue));
settings.endGroup(); // ParamId
}
} else {
foreach (const QString &paramTypeIdString, settings.allKeys()) {
params.append(Param(ParamTypeId(paramTypeIdString), settings.value(paramTypeIdString)));
foreach (const ParamType &paramType, thingClass.settingsTypes()) {
QVariant value = paramType.defaultValue();
if (settings.contains(paramType.id().toString())) {
value = settings.value(paramType.id().toString());
} else if (settings.childGroups().contains(paramType.id().toString())) {
// 0.12.2 - 0.22 used to store in subgroups
settings.beginGroup(paramType.id().toString());
value = settings.value("value");
settings.endGroup(); // paramTypeId
}
value.convert(paramType.type());
Param param(paramType.id(), value);
thingSettings.append(param);
}
// Fill in any missing params with defaults
thingSettings = buildParams(thingClass.settingsTypes(), thingSettings);
settings.endGroup(); // Settings
thing->setSettings(thingSettings);
settings.endGroup(); // Settings
settings.endGroup(); // ThingId
// We always add the thing to the list in this case. If it's in the stored things
@ -1626,19 +1607,13 @@ void ThingManagerImplementation::storeConfiguredThings()
settings.beginGroup("Params");
foreach (const Param &param, thing->params()) {
settings.beginGroup(param.paramTypeId().toString());
settings.setValue("type", static_cast<int>(param.value().type()));
settings.setValue("value", param.value());
settings.endGroup(); // ParamTypeId
settings.setValue(param.paramTypeId().toString(), param.value());
}
settings.endGroup(); // Params
settings.beginGroup("Settings");
foreach (const Param &param, thing->settings()) {
settings.beginGroup(param.paramTypeId().toString());
settings.setValue("type", static_cast<int>(param.value().type()));
settings.setValue("value", param.value());
settings.endGroup(); // ParamTypeId
settings.setValue(param.paramTypeId().toString(), param.value());
}
settings.endGroup(); // Settings
@ -1726,19 +1701,19 @@ void ThingManagerImplementation::onAutoThingDisappeared(const ThingId &thingId)
Thing *thing = m_configuredThings.value(thingId);
if (!thing) {
qWarning(dcThingManager) << "Received an autoThingDisappeared signal but this thing is unknown:" << thingId;
qCWarning(dcThingManager) << "Received an autoThingDisappeared signal but this thing is unknown:" << thingId;
return;
}
ThingClass thingClass = m_supportedThings.value(thing->thingClassId());
if (thingClass.pluginId() != plugin->pluginId()) {
qWarning(dcThingManager) << "Received a autoThingDisappeared signal but emitting plugin does not own the thing";
qCWarning(dcThingManager) << "Received a autoThingDisappeared signal but emitting plugin does not own the thing";
return;
}
if (!thing->autoCreated()) {
qWarning(dcThingManager) << "Received an autoThingDisappeared signal but thing creationMethod is not CreateMothodAuto";
qCWarning(dcThingManager) << "Received an autoThingDisappeared signal but thing creationMethod is not CreateMothodAuto";
return;
}
@ -1940,17 +1915,25 @@ void ThingManagerImplementation::slotThingNameChanged()
emit thingChanged(thing);
}
// Merges params from first and second. First has higher priority than second. If neither are given, the default is used - if any
ParamList ThingManagerImplementation::buildParams(const ParamTypes &types, const ParamList &first, const ParamList &second)
{
// Merge params from discovered descriptor and additional overrides provided on API call. User provided params have higher priority than discovery params.
ParamList finalParams;
foreach (const ParamType &paramType, types) {
QVariant value;
if (first.hasParam(paramType.id())) {
finalParams.append(Param(paramType.id(), first.paramValue(paramType.id())));
value = first.paramValue(paramType.id());
} else if (second.hasParam(paramType.id())) {
finalParams.append(Param(paramType.id(), second.paramValue(paramType.id())));
value = second.paramValue(paramType.id());
} else if (paramType.defaultValue().isValid()){
finalParams.append(Param(paramType.id(), paramType.defaultValue()));
value = paramType.defaultValue();
}
if (!value.isNull()) {
bool success = value.convert(paramType.type());
if (!success) {
qCWarning(dcThingManager()) << "Type mismatch in param" << paramType.name() << "for value" << value;
}
finalParams.append(Param(paramType.id(), value));
}
}
return finalParams;
@ -2004,13 +1987,6 @@ ThingSetupInfo* ThingManagerImplementation::setupThing(Thing *thing)
ThingClass thingClass = findThingClass(thing->thingClassId());
IntegrationPlugin *plugin = m_integrationPlugins.value(thingClass.pluginId());
if (!plugin) {
qCWarning(dcThingManager) << "Can't find a plugin for this thing" << thing;
ThingSetupInfo *info = new ThingSetupInfo(thing, this);
info->finish(Thing::ThingErrorPluginNotFound, tr("The plugin for this thing is not loaded."));
return info;
}
QList<State> states;
foreach (const StateType &stateType, thingClass.stateTypes()) {
State state(stateType.id(), thing->id());
@ -2023,8 +1999,14 @@ ThingSetupInfo* ThingManagerImplementation::setupThing(Thing *thing)
connect(thing, &Thing::settingChanged, this, &ThingManagerImplementation::slotThingSettingChanged);
connect(thing, &Thing::nameChanged, this, &ThingManagerImplementation::slotThingNameChanged);
ThingSetupInfo *info = new ThingSetupInfo(thing, this, 30000);
if (!plugin) {
qCWarning(dcThingManager) << "Can't find a plugin for this thing" << thing;
info->finish(Thing::ThingErrorPluginNotFound, tr("The plugin for this thing is not loaded."));
return info;
}
plugin->setupThing(info);
return info;
@ -2045,16 +2027,17 @@ void ThingManagerImplementation::loadThingStates(Thing *thing)
ThingClass thingClass = m_supportedThings.value(thing->thingClassId());
foreach (const StateType &stateType, thingClass.stateTypes()) {
if (stateType.cached()) {
QVariant value;
// First try to load new style
if (settings.childGroups().contains(stateType.id().toString())) {
QVariant value(stateType.defaultValue());
if (settings.contains(stateType.id().toString())) {
value = settings.value(stateType.id().toString());
} else if (settings.childGroups().contains(stateType.id().toString())) {
// 0.9 - 0.22 used to store in a subgroup
settings.beginGroup(stateType.id().toString());
value = settings.value("value", stateType.defaultValue());
value.convert(settings.value("type").toInt());
value = settings.value("value");
settings.endGroup();
} else { // Try to fall back to the pre 0.9.0 way of storing states
value = settings.value(stateType.id().toString(), stateType.defaultValue());
}
value.convert(stateType.type());
thing->setStateValue(stateType.id(), value);
} else {
thing->setStateValue(stateType.id(), stateType.defaultValue());
@ -2125,12 +2108,7 @@ void ThingManagerImplementation::storeThingStates(Thing *thing)
settings.beginGroup(thing->id().toString());
ThingClass thingClass = m_supportedThings.value(thing->thingClassId());
foreach (const StateType &stateType, thingClass.stateTypes()) {
if (stateType.cached()) {
settings.beginGroup(stateType.id().toString());
settings.setValue("type", static_cast<int>(thing->stateValue(stateType.id()).type()));
settings.setValue("value", thing->stateValue(stateType.id()));
settings.endGroup();
}
settings.setValue(stateType.id().toString(), thing->stateValue(stateType.id()));
}
settings.endGroup();
}

View File

@ -359,17 +359,28 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
stateType.setUnit(unitVerification.second);
}
stateType.setDefaultValue(st.value("defaultValue").toVariant());
if (st.contains("minValue"))
stateType.setMinValue(st.value("minValue").toVariant());
QVariant defaultValue = st.value("defaultValue").toVariant();
defaultValue.convert(stateType.type());
stateType.setDefaultValue(defaultValue);
if (st.contains("maxValue"))
stateType.setMaxValue(st.value("maxValue").toVariant());
if (st.contains("minValue")) {
QVariant minValue = st.value("minValue").toVariant();
minValue.convert(stateType.type());
stateType.setMinValue(minValue);
}
if (st.contains("maxValue")) {
QVariant maxValue = st.value("maxValue").toVariant();
maxValue.convert(stateType.type());
stateType.setMaxValue(maxValue);
}
if (st.contains("possibleValues")) {
QVariantList possibleValues;
foreach (const QJsonValue &possibleValueJson, st.value("possibleValues").toArray()) {
possibleValues.append(possibleValueJson.toVariant());
QVariant possibleValue = possibleValueJson.toVariant();
possibleValue.convert(stateType.type());
possibleValues.append(possibleValue);
}
stateType.setPossibleValues(possibleValues);
@ -824,7 +835,13 @@ QPair<bool, ParamTypes> PluginMetadata::parseParamTypes(const QJsonArray &array)
m_validationErrors.append("Param type \"" + paramName + "\" has duplicate UUID: " + paramTypeId.toString());
hasErrors = true;
}
ParamType paramType(paramTypeId, paramName, t, pt.value("defaultValue").toVariant());
QVariant defaultValue = pt.value("defaultValue").toVariant();
if (!defaultValue.isNull()) {
// Only convert if there actually is a value as we want it to be null if it isn't specced
// explicitly and convert() would initialize it to the variant's default value
defaultValue.convert(t);
}
ParamType paramType(paramTypeId, paramName, t, defaultValue);
paramType.setDisplayName(pt.value("displayName").toString());
@ -861,7 +878,19 @@ QPair<bool, ParamTypes> PluginMetadata::parseParamTypes(const QJsonArray &array)
paramType.setReadOnly(pt.value("readOnly").toBool());
paramType.setAllowedValues(allowedValues);
paramType.setLimits(pt.value("minValue").toVariant(), pt.value("maxValue").toVariant());
QVariant minValue = pt.value("minValue").toVariant();
if (!minValue.isNull()) {
// Only convert if there actually is a value as we want it to be null if it isn't specced
// explicitly and convert() would initialize it to the variant's default value
minValue.convert(t);
}
QVariant maxValue = pt.value("maxValue").toVariant();
if (!maxValue.isNull()) {
// Only convert if there actually is a value as we want it to be null if it isn't specced
// explicitly and convert() would initialize it to the variant's default value
maxValue.convert(t);
}
paramType.setLimits(minValue, maxValue);
paramType.setIndex(index++);
paramTypes.append(paramType);
}

View File

@ -327,23 +327,49 @@ QVariant Thing::stateValue(const StateTypeId &stateTypeId) const
/*! For convenience, this finds the \l{State} matching the given \a stateTypeId in this thing and sets the current value to \a value. */
void Thing::setStateValue(const StateTypeId &stateTypeId, const QVariant &value)
{
StateType stateType = m_thingClass.stateTypes().findById(stateTypeId);
if (!stateType.isValid()) {
qCWarning(dcThing()) << "No such state type" << stateTypeId.toString() << "in" << m_name << "(" + thingClass().name() + ")";
return;
}
for (int i = 0; i < m_states.count(); ++i) {
if (m_states.at(i).stateTypeId() == stateTypeId) {
if (m_states.at(i).value() == value)
return;
QVariant newValue = value;
if (!newValue.convert(stateType.type())) {
qCWarning(dcThing()).nospace() << m_name << ": Invalid value " << value << " for state " << stateType.name() << ". Type mismatch. Expected type: " << QVariant::typeToName(stateType.type()) << " (Discarding change)";
return;
}
if (stateType.minValue().isValid() && value < stateType.minValue()) {
qCWarning(dcThing()).nospace() << m_name << ": Invalid value " << value << " for state " << stateType.name() << ". Out of range: " << stateType.minValue() << " - " << stateType.maxValue() << " (Correcting to closest value within range)";
newValue = stateType.minValue();
}
if (stateType.maxValue().isValid() && value > stateType.maxValue()) {
qCWarning(dcThing()).nospace() << m_name << ": Invalid value " << value << " for state " << stateType.name() << ". Out of range: " << stateType.minValue() << " - " << stateType.maxValue() << " (Correcting to closest value within range)";
newValue = stateType.maxValue();
}
if (!stateType.possibleValues().isEmpty() && !stateType.possibleValues().contains(value)) {
qCWarning(dcThing()).nospace() << m_name << ": Invalid value " << value << " for state " << stateType.name() << ". Not an accepted value. Possible values: " << stateType.possibleValues() << " (Discarding change)";
return;
}
// TODO: check min/max value + possible values
// to prevent an invalid state type from the plugin side
QVariant oldValue = m_states.at(i).value();
State newState(stateTypeId, m_id);
newState.setValue(value);
m_states[i] = newState;
if (oldValue == newValue) {
qCDebug(dcThing()).nospace() << m_name << ": Discarding state change for " << stateType.name() << " as the value did not actually change. Old value:" << oldValue << "New value:" << newValue;
return;
}
qCDebug(dcThing()).nospace() << m_name << ": State " << stateType.name() << " changed from " << oldValue << " to " << newValue;
m_states[i].setValue(newValue);
emit stateValueChanged(stateTypeId, value);
return;
}
}
qCWarning(dcThingManager) << "Failed setting state for" << m_name << value;
Q_ASSERT_X(false, m_name.toUtf8(), QString("Failed setting state %1 to %2").arg(stateType.name()).arg(value.toString()).toUtf8());
qCWarning(dcThing).nospace() << m_name << ": Failed setting state " << stateType.name() << "to" << value;
}
/*! Returns the \l{State} with the given \a stateTypeId of this thing. */

View File

@ -41,8 +41,7 @@ ThingUtils::ThingUtils()
}
/*! Verify if the given \a params matches the given \a paramTypes. Ith \a requireAll
* is true, all \l{ParamList}{Params} has to be valid. Returns \l{Device::DeviceError} to inform about the result.*/
/*! Verify if the given \a params matches the given \a paramTypes.*/
Thing::ThingError ThingUtils::verifyParams(const QList<ParamType> paramTypes, const ParamList &params)
{
foreach (const Param &param, params) {
@ -52,7 +51,7 @@ Thing::ThingError ThingUtils::verifyParams(const QList<ParamType> paramTypes, co
}
}
foreach (const ParamType &paramType, paramTypes) {
bool found = false;
bool found = !paramType.defaultValue().isNull();
foreach (const Param &param, params) {
if (paramType.id() == param.paramTypeId()) {
found = true;

View File

@ -171,11 +171,11 @@ bool StateDescriptor::operator ==(const State &state) const
if ((m_stateTypeId != state.stateTypeId()) || (m_thingId != state.thingId())) {
return false;
}
if (!m_stateValue.canConvert(state.value().type())) {
QVariant convertedValue = m_stateValue;
bool res = convertedValue.convert(state.value().type());
if (!res) {
return false;
}
QVariant convertedValue = m_stateValue;
convertedValue.convert(state.value().type());
switch (m_operatorType) {
case Types::ValueOperatorEquals:
return convertedValue == state.value();

View File

@ -222,6 +222,12 @@ QStringList StateType::mandatoryTypeProperties()
return QStringList() << "id" << "name" << "displayName" << "displayNameEvent" << "type" << "defaultValue";
}
/*! Returns true if this state type has an ID, a type and a name set. */
bool StateType::isValid() const
{
return !m_id.isNull() && m_type != QVariant::Invalid && !m_name.isEmpty();
}
StateTypes::StateTypes(const QList<StateType> &other)
{
foreach (const StateType &st, other) {

View File

@ -96,6 +96,8 @@ public:
static QStringList typeProperties();
static QStringList mandatoryTypeProperties();
bool isValid() const;
private:
StateTypeId m_id;
QString m_name;

View File

@ -98,7 +98,7 @@
"name": "double",
"displayName": "Dummy double state",
"displayNameEvent": "Dummy double state changed",
"type": "int",
"type": "double",
"minValue": 0,
"maxValue": 100,
"defaultValue": 2.7

View File

@ -85,7 +85,6 @@ void TestActions::executeAction()
params.insert("deviceId", deviceId);
params.insert("params", actionParams);
QVariant response = injectAndWait("Actions.ExecuteAction", params);
qDebug() << "executeActionresponse" << response;
verifyError(response, "deviceError", enumValueName(error));
// Fetch action execution history from mock device

View File

@ -382,10 +382,10 @@ void TestDevices::addConfiguredDevice_data()
QTest::newRow("User, JustAdd, wrong param") << mockThingClassId << invalidDeviceParams << true << Device::DeviceErrorInvalidParameter;
deviceParams.clear(); deviceParams << httpportParam << fakeparam;
QTest::newRow("USer, JustAdd, additional invalid param") << mockThingClassId << deviceParams << false << Device::DeviceErrorNoError;
QTest::newRow("User, JustAdd, additional invalid param") << mockThingClassId << deviceParams << false << Device::DeviceErrorInvalidParameter;
deviceParams.clear(); deviceParams << httpportParam << fakeparam2;
QTest::newRow("USer, JustAdd, additional param, valid but unused") << mockThingClassId << deviceParams << true << Device::DeviceErrorNoError;
QTest::newRow("User, JustAdd, duplicate param") << mockThingClassId << deviceParams << true << Device::DeviceErrorInvalidParameter;
}

View File

@ -382,10 +382,10 @@ void TestIntegrations::addThing_data()
QTest::newRow("User, JustAdd, wrong param") << mockThingClassId << invalidThingParams << true << Thing::ThingErrorInvalidParameter;
thingParams.clear(); thingParams << httpportParam << fakeparam;
QTest::newRow("USer, JustAdd, additional invalid param") << mockThingClassId << thingParams << false << Thing::ThingErrorNoError;
QTest::newRow("USer, JustAdd, additional invalid param") << mockThingClassId << thingParams << false << Thing::ThingErrorInvalidParameter;
thingParams.clear(); thingParams << httpportParam << fakeparam2;
QTest::newRow("USer, JustAdd, additional param, valid but unused") << mockThingClassId << thingParams << true << Thing::ThingErrorNoError;
QTest::newRow("USer, JustAdd, duplicate param") << mockThingClassId << thingParams << true << Thing::ThingErrorInvalidParameter;
}

View File

@ -265,13 +265,14 @@ void TestRules::setWritableStateValue(const ThingId &thingId, const StateTypeId
QVariantList stateChangedVariants = checkNotifications(stateSpy, "Integrations.StateChanged");
QVERIFY2(stateChangedVariants.count() == 1, "Did not get Integrations.StateChanged notification.");
qCDebug(dcTests()) << "Notification content:" << qUtf8Printable(QJsonDocument::fromVariant(stateChangedVariants).toJson());
QVariantMap notification = stateChangedVariants.first().toMap().value("params").toMap();
QVERIFY2(notification.contains("thingId"), "Integrations.StateChanged notification does not contain thingId");
QVERIFY2(ThingId(notification.value("thingId").toString()) == thingId, "Integrations.StateChanged notification does not contain the correct thingId");
QVERIFY2(notification.contains("stateTypeId"), "Integrations.StateChanged notification does not contain stateTypeId");
QVERIFY2(StateTypeId(notification.value("stateTypeId").toString()) == stateTypeId, "Integrations.StateChanged notification does not contain the correct stateTypeId");
QVERIFY2(notification.contains("value"), "Integrations.StateChanged notification does not contain new state value");
QVERIFY2(notification.value("value") == QVariant(value), "Integrations.StateChanged notification does not contain the new value");
QVERIFY2(notification.value("value") == QVariant(value), QString("Integrations.StateChanged notification does not contain the new value. Got: %1, Expected: %2").arg(notification.value("value").toString()).arg(QVariant(value).toString()).toUtf8());
}
}
@ -1990,11 +1991,11 @@ void TestRules::testChildEvaluator_data()
QTest::addColumn<bool>("trigger");
QTest::addColumn<bool>("active");
QTest::newRow("Unchanged | 2 | 2.5 | String value 1 | #FF0000") << testThingId << ruleMap << 2 << 2.5 << "String value 1" << "#FF0000" << false << false;
QTest::newRow("Unchanged | 60 | 2.5 | String value 2 | #FF0000") << testThingId << ruleMap << 60 << 2.5 << "String value 2" << "#FF0000" << false << false;
QTest::newRow("Unchanged | 60 | 20.5 | String value 2 | #FF0000") << testThingId << ruleMap << 60 << 20.5 << "String value 2" << "#FF0000" << false << false;
QTest::newRow("Active | 60 | 20.5 | String value 2 | #00FF00") << testThingId << ruleMap << 60 << 20.5 << "String value 2" << "#00FF00" << true << true;
QTest::newRow("Active | 60 | 20.5 | String value 2 | #00FF00") << testThingId << ruleMap << 60 << 20.5 << "String value 2" << "#00FF00" << true << true;
QTest::newRow("Unchanged | 2 | 2.5 | String value 1 | #FF0000") << testThingId << ruleMap << 2 << 2.5 << "String value 1" << "#ff0000" << false << false;
QTest::newRow("Unchanged | 60 | 2.5 | String value 2 | #FF0000") << testThingId << ruleMap << 60 << 2.5 << "String value 2" << "#ff0000" << false << false;
QTest::newRow("Unchanged | 60 | 20.5 | String value 2 | #FF0000") << testThingId << ruleMap << 60 << 20.5 << "String value 2" << "#ff0000" << false << false;
QTest::newRow("Active | 60 | 20.5 | String value 2 | #00FF00") << testThingId << ruleMap << 60 << 20.5 << "String value 2" << "#00ff00" << true << true;
QTest::newRow("Active | 60 | 20.5 | String value 2 | #00FF00") << testThingId << ruleMap << 60 << 20.5 << "String value 2" << "#00ff00" << true << true;
}
void TestRules::testChildEvaluator()

View File

@ -118,7 +118,7 @@ protected:
// just for debugging
inline void printJson(const QVariant &response) {
QJsonDocument jsonDoc = QJsonDocument::fromVariant(response);
qDebug() << jsonDoc.toJson();
qCDebug(dcTests()) << jsonDoc.toJson();
}
void waitForDBSync();