From ef79b3bb7cd744e85f4a75e8cc301ee4b5b1b5ff Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 8 Dec 2023 22:04:51 +0100 Subject: [PATCH] [Formula] Apply formula when updating taskvalues --- src/_P152_DAC.ino | 3 +- src/src/DataStructs/Caches.cpp | 20 +- src/src/DataStructs/Caches.h | 5 +- src/src/DataStructs/UserVarStruct.cpp | 234 ++++++++++++++++----- src/src/DataStructs/UserVarStruct.h | 11 +- src/src/ESPEasyCore/Controller.cpp | 49 +---- src/src/Globals/Plugins.cpp | 1 + src/src/Helpers/ESPEasy_Storage.cpp | 3 + src/src/PluginStructs/P109_data_struct.cpp | 2 +- src/src/WebServer/DevicesPage.cpp | 2 + 10 files changed, 223 insertions(+), 107 deletions(-) diff --git a/src/_P152_DAC.ino b/src/_P152_DAC.ino index 65c2cee3f..2bc26b34c 100644 --- a/src/_P152_DAC.ino +++ b/src/_P152_DAC.ino @@ -19,6 +19,7 @@ #define PLUGIN_VALUENAME1_152 "Output" #define P152_DAC_VALUE UserVar[event->BaseVarIndex] +#define P152_SET_DAC_VALUE(x) UserVar.setFloat(event->TaskIndex, 0, x) #if !(defined(ESP32_CLASSIC) || defined(ESP32S2)) # error P152 ESP32 DAC not supported on this CPU type! @@ -94,7 +95,7 @@ boolean Plugin_152(uint8_t function, struct EventStruct *event, String& string) if (getDAC_gpio_info(CONFIG_PIN1, dac) && (dac == event->Par1)) { int value = min(255, max(0, event->Par2)); // Limit value - P152_DAC_VALUE = value; + P152_SET_DAC_VALUE(value); dacWrite(CONFIG_PIN1, value); // Set output value addLog(LOG_LEVEL_INFO, formatGpioName_DAC(CONFIG_PIN1) + diff --git a/src/src/DataStructs/Caches.cpp b/src/src/DataStructs/Caches.cpp index 0d8811114..902339e8e 100644 --- a/src/src/DataStructs/Caches.cpp +++ b/src/src/DataStructs/Caches.cpp @@ -151,6 +151,20 @@ String Caches::getTaskDeviceValueName(taskIndex_t TaskIndex, uint8_t rel_index) return EMPTY_STRING; } +bool Caches::hasFormula(taskIndex_t TaskIndex, uint8_t rel_index) +{ + if (validTaskIndex(TaskIndex)) { + // Just a quick test to see if we do have a formula present. + // Task Formula are not used very often, so we will probably almost always have to return an empty string. + auto it = getExtraTaskSettings(TaskIndex); + + if (it != extraTaskSettings_cache.end()) { + return bitRead(it->second.hasFormula, rel_index); + } + } + return false; +} + bool Caches::hasFormula(taskIndex_t TaskIndex) { if (validTaskIndex(TaskIndex)) { @@ -159,7 +173,7 @@ bool Caches::hasFormula(taskIndex_t TaskIndex) auto it = getExtraTaskSettings(TaskIndex); if (it != extraTaskSettings_cache.end()) { - return it->second.hasFormula; + return it->second.hasFormula != 0; } } return false; @@ -167,7 +181,7 @@ bool Caches::hasFormula(taskIndex_t TaskIndex) String Caches::getTaskDeviceFormula(taskIndex_t TaskIndex, uint8_t rel_index) { - if ((rel_index < VARS_PER_TASK) && hasFormula(TaskIndex)) { + if ((rel_index < VARS_PER_TASK) && bitRead(hasFormula(TaskIndex), rel_index)) { #ifdef ESP32 auto it = getExtraTaskSettings(TaskIndex); @@ -297,7 +311,7 @@ void Caches::updateExtraTaskSettingsCache() #ifdef ESP32 move_special(tmp.TaskDeviceFormula[i], String(ExtraTaskSettings.TaskDeviceFormula[i])); #endif - tmp.hasFormula = true; + bitSet(tmp.hasFormula, i); } tmp.decimals[i] = ExtraTaskSettings.TaskDeviceValueDecimals[i]; #if FEATURE_PLUGIN_STATS diff --git a/src/src/DataStructs/Caches.h b/src/src/DataStructs/Caches.h index b8c960ed7..99a309997 100644 --- a/src/src/DataStructs/Caches.h +++ b/src/src/DataStructs/Caches.h @@ -51,7 +51,7 @@ struct ExtraTaskSettings_cache_t { #if FEATURE_PLUGIN_STATS PluginStats_Config_t pluginStatsConfig[VARS_PER_TASK] = {}; #endif // if FEATURE_PLUGIN_STATS - bool hasFormula = false; + uint8_t hasFormula = 0; // Bitmap which task value has formula }; typedef std::map TaskIndexNameMap; @@ -86,7 +86,8 @@ struct Caches { uint8_t rel_index); // Check to see if at least one of the taskvalues has a non-empty formula field. - bool hasFormula(taskIndex_t TaskIndex); + bool hasFormula(taskIndex_t TaskIndex, uint8_t rel_index); + bool hasFormula(taskIndex_t TaskIndex); String getTaskDeviceFormula(taskIndex_t TaskIndex, diff --git a/src/src/DataStructs/UserVarStruct.cpp b/src/src/DataStructs/UserVarStruct.cpp index 927c4820d..cd5b79b42 100644 --- a/src/src/DataStructs/UserVarStruct.cpp +++ b/src/src/DataStructs/UserVarStruct.cpp @@ -1,9 +1,16 @@ #include "../DataStructs/UserVarStruct.h" +#include "../DataStructs/TimingStats.h" + #include "../ESPEasyCore/ESPEasy_Log.h" +#include "../Globals/Cache.h" #include "../Globals/Plugins.h" +#include "../Globals/RulesCalculate.h" #include "../Helpers/_Plugin_SensorTypeHelper.h" #include "../Helpers/CRC_functions.h" +#include "../Helpers/StringParser.h" + + UserVarStruct::UserVarStruct() { @@ -23,38 +30,43 @@ void UserVarStruct::clear() // Implementation of [] operator. This function must return a // reference as array element can be put on left side + /* -float& UserVarStruct::operator[](unsigned int index) + float& UserVarStruct::operator[](unsigned int index) + { + const unsigned int taskIndex = index / VARS_PER_TASK; + const unsigned int varNr = index % VARS_PER_TASK; + + if (taskIndex >= _data.size()) { + static float errorvalue = NAN; + addLog(LOG_LEVEL_ERROR, F("UserVar index out of range")); + return errorvalue; + } + return _data.at(taskIndex).floats[varNr]; + } + */ +float UserVarStruct::operator[](unsigned int index) const { const unsigned int taskIndex = index / VARS_PER_TASK; const unsigned int varNr = index % VARS_PER_TASK; - if (taskIndex >= _data.size()) { + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->getFloat(varNr); + } else { static float errorvalue = NAN; addLog(LOG_LEVEL_ERROR, F("UserVar index out of range")); return errorvalue; } - return _data.at(taskIndex).floats[varNr]; -} -*/ - -const float& UserVarStruct::operator[](unsigned int index) const -{ - const unsigned int taskIndex = index / VARS_PER_TASK; - const unsigned int varNr = index % VARS_PER_TASK; - - if (taskIndex >= _data.size()) { - static float errorvalue = NAN; - addLog(LOG_LEVEL_ERROR, F("UserVar index out of range")); - return errorvalue; - } - return _data.at(taskIndex).floats[varNr]; } unsigned long UserVarStruct::getSensorTypeLong(taskIndex_t taskIndex) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].getSensorTypeLong(); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, 0); + + if (data != nullptr) { + return data->getSensorTypeLong(); } return 0u; } @@ -62,7 +74,12 @@ unsigned long UserVarStruct::getSensorTypeLong(taskIndex_t taskIndex) const void UserVarStruct::setSensorTypeLong(taskIndex_t taskIndex, unsigned long value) { if (taskIndex < _data.size()) { - _data[taskIndex].setSensorTypeLong(value); + if (Cache.hasFormula(taskIndex, 0)) { + const ESPEASY_RULES_FLOAT_TYPE tmp = value; + applyFormula(taskIndex, 0, tmp, Sensor_VType::SENSOR_TYPE_ULONG); + } else { + _data[taskIndex].setSensorTypeLong(value); + } } } @@ -71,8 +88,10 @@ void UserVarStruct::setSensorTypeLong(taskIndex_t taskIndex, unsigned long value int32_t UserVarStruct::getInt32(taskIndex_t taskIndex, uint8_t varNr) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].getInt32(varNr); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->getInt32(varNr); } return 0; } @@ -82,7 +101,12 @@ void UserVarStruct::setInt32(taskIndex_t taskIndex, int32_t value) { if (taskIndex < _data.size()) { - _data[taskIndex].setInt32(varNr, value); + if (Cache.hasFormula(taskIndex, varNr)) { + const ESPEASY_RULES_FLOAT_TYPE tmp = value; + applyFormula(taskIndex, varNr, tmp, Sensor_VType::SENSOR_TYPE_INT32_QUAD); + } else { + _data[taskIndex].setInt32(varNr, value); + } } } @@ -90,8 +114,10 @@ void UserVarStruct::setInt32(taskIndex_t taskIndex, uint32_t UserVarStruct::getUint32(taskIndex_t taskIndex, uint8_t varNr) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].getUint32(varNr); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->getUint32(varNr); } return 0u; } @@ -99,7 +125,12 @@ uint32_t UserVarStruct::getUint32(taskIndex_t taskIndex, uint8_t varNr) const void UserVarStruct::setUint32(taskIndex_t taskIndex, uint8_t varNr, uint32_t value) { if (taskIndex < _data.size()) { - _data[taskIndex].setUint32(varNr, value); + if (Cache.hasFormula(taskIndex, varNr)) { + const ESPEASY_RULES_FLOAT_TYPE tmp = value; + applyFormula(taskIndex, varNr, tmp, Sensor_VType::SENSOR_TYPE_UINT32_QUAD); + } else { + _data[taskIndex].setUint32(varNr, value); + } } } @@ -108,8 +139,10 @@ void UserVarStruct::setUint32(taskIndex_t taskIndex, uint8_t varNr, uint32_t val int64_t UserVarStruct::getInt64(taskIndex_t taskIndex, uint8_t varNr) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].getInt64(varNr); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->getInt64(varNr); } return 0; } @@ -119,15 +152,22 @@ void UserVarStruct::setInt64(taskIndex_t taskIndex, int64_t value) { if (taskIndex < _data.size()) { - _data[taskIndex].setInt64(varNr, value); + if (Cache.hasFormula(taskIndex, varNr)) { + const ESPEASY_RULES_FLOAT_TYPE tmp = value; + applyFormula(taskIndex, varNr, tmp, Sensor_VType::SENSOR_TYPE_INT64_DUAL); + } else { + _data[taskIndex].setInt64(varNr, value); + } } } uint64_t UserVarStruct::getUint64(taskIndex_t taskIndex, uint8_t varNr) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].getUint64(varNr); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->getUint64(varNr); } return 0u; } @@ -137,7 +177,12 @@ void UserVarStruct::setUint64(taskIndex_t taskIndex, uint64_t value) { if (taskIndex < _data.size()) { - _data[taskIndex].setUint64(varNr, value); + if (Cache.hasFormula(taskIndex, varNr)) { + const ESPEASY_RULES_FLOAT_TYPE tmp = value; + applyFormula(taskIndex, varNr, tmp, Sensor_VType::SENSOR_TYPE_UINT64_DUAL); + } else { + _data[taskIndex].setUint64(varNr, value); + } } } @@ -146,8 +191,10 @@ void UserVarStruct::setUint64(taskIndex_t taskIndex, float UserVarStruct::getFloat(taskIndex_t taskIndex, uint8_t varNr) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].getFloat(varNr); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->getFloat(varNr); } return 0.0f; } @@ -157,17 +204,24 @@ void UserVarStruct::setFloat(taskIndex_t taskIndex, float value) { if (taskIndex < _data.size()) { - _data[taskIndex].setFloat(varNr, value); + if (Cache.hasFormula(taskIndex, varNr)) { + const ESPEASY_RULES_FLOAT_TYPE tmp = value; + applyFormula(taskIndex, varNr, tmp, Sensor_VType::SENSOR_TYPE_QUAD); + } else { + _data[taskIndex].setFloat(varNr, value); + } } } #if FEATURE_EXTENDED_TASK_VALUE_TYPES -#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE double UserVarStruct::getDouble(taskIndex_t taskIndex, uint8_t varNr) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].getDouble(varNr); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->getDouble(varNr); } return 0.0; } @@ -177,43 +231,52 @@ void UserVarStruct::setDouble(taskIndex_t taskIndex, double value) { if (taskIndex < _data.size()) { - _data[taskIndex].setDouble(varNr, value); + if (Cache.hasFormula(taskIndex, varNr)) { + applyFormula(taskIndex, varNr, value, Sensor_VType::SENSOR_TYPE_DOUBLE_DUAL); + } else { + _data[taskIndex].setDouble(varNr, value); + } } } -#endif -#endif // if FEATURE_EXTENDED_TASK_VALUE_TYPES + +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +#endif // if FEATURE_EXTENDED_TASK_VALUE_TYPES ESPEASY_RULES_FLOAT_TYPE UserVarStruct::getAsDouble(taskIndex_t taskIndex, - uint8_t varNr, - Sensor_VType sensorType) const + uint8_t varNr, + Sensor_VType sensorType) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].getAsDouble(varNr, sensorType); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->getAsDouble(varNr, sensorType); } return 0.0; } String UserVarStruct::getAsString(taskIndex_t taskIndex, uint8_t varNr, Sensor_VType sensorType, uint8_t nrDecimals) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].getAsString(varNr, sensorType, nrDecimals); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->getAsString(varNr, sensorType, nrDecimals); } return EMPTY_STRING; } void UserVarStruct::set(taskIndex_t taskIndex, uint8_t varNr, const ESPEASY_RULES_FLOAT_TYPE& value, Sensor_VType sensorType) { - if (taskIndex < _data.size()) { - _data[taskIndex].set(varNr, value, sensorType); - } + applyFormula(taskIndex, varNr, value, sensorType); } bool UserVarStruct::isValid(taskIndex_t taskIndex, uint8_t varNr, Sensor_VType sensorType) const { - if (taskIndex < _data.size()) { - return _data[taskIndex].isValid(varNr, sensorType); + const TaskValues_Data_t *data = getRawOrComputed(taskIndex, varNr); + + if (data != nullptr) { + return data->isValid(varNr, sensorType); } return false; } @@ -247,3 +310,70 @@ uint32_t UserVarStruct::compute_CRC32() const return calc_CRC32(buffer, size); } + +void UserVarStruct::clear_computed(taskIndex_t taskIndex) +{ + if (!Cache.hasFormula(taskIndex)) { + auto it = _computed.find(taskIndex); + + if (it != _computed.end()) { + _computed.erase(it); + } + } +} + +const TaskValues_Data_t * UserVarStruct::getRawOrComputed(taskIndex_t taskIndex, uint8_t varNr) const +{ + if (Cache.hasFormula(taskIndex, varNr)) { + auto it = _computed.find(taskIndex); + + if (it != _computed.end()) { + return &(it->second); + } + } + return getTaskValues_Data(taskIndex); +} + +void UserVarStruct::applyFormula(taskIndex_t taskIndex, + uint8_t varNr, + const ESPEASY_RULES_FLOAT_TYPE& value, + Sensor_VType sensorType) +{ + if ((taskIndex >= _data.size()) || (varNr >= VARS_PER_TASK)) { + return; + } + String formula = Cache.getTaskDeviceFormula(taskIndex, varNr); + + if (!formula.isEmpty()) + { + START_TIMER; + + // TD-er: Should we use the set nr of decimals here, or not round at all? + // See: https://github.com/letscontrolit/ESPEasy/issues/3721#issuecomment-889649437 + const int nrDecimals = Cache.getTaskDeviceValueDecimals(taskIndex, varNr); + + if (formula.indexOf(F("%pvalue%")) != -1) { + formula.replace(F("%pvalue%"), getAsString(taskIndex, varNr, sensorType, nrDecimals)); + } + + if (formula.indexOf(F("%value%")) != -1) + { + // Use a temporary TaskValues_Data_t object to have uniform formatting + TaskValues_Data_t tmp; + tmp.set(varNr, value, sensorType); + formula.replace(F("%value%"), tmp.getAsString(varNr, sensorType, nrDecimals)); + } + + ESPEASY_RULES_FLOAT_TYPE result{}; + + if (!isError(Calculate(parseTemplate(formula), result))) { + _computed[taskIndex].set(varNr, result, sensorType); + } else { + // FIXME TD-er: What to do now? Just copy the raw value, set error value or don't update? + } + + STOP_TIMER(COMPUTE_FORMULA_STATS); + } + + _data[taskIndex].set(varNr, value, sensorType); +} diff --git a/src/src/DataStructs/UserVarStruct.h b/src/src/DataStructs/UserVarStruct.h index 390e08a9d..969db62b4 100644 --- a/src/src/DataStructs/UserVarStruct.h +++ b/src/src/DataStructs/UserVarStruct.h @@ -20,7 +20,7 @@ struct UserVarStruct { /* float & operator[](unsigned int index); */ - const float & operator[](unsigned int index) const; + float operator[](unsigned int index) const; // Legacy "long" type, which was spread over several floats. unsigned long getSensorTypeLong(taskIndex_t taskIndex) const; @@ -104,8 +104,17 @@ struct UserVarStruct { uint32_t compute_CRC32() const; + void clear_computed(taskIndex_t taskIndex); + private: + const TaskValues_Data_t* getRawOrComputed(taskIndex_t taskIndex, uint8_t varNr) const; + + void applyFormula(taskIndex_t taskIndex, + uint8_t varNr, + const ESPEASY_RULES_FLOAT_TYPE& value, + Sensor_VType sensorType); + // Raw TaskValues data as stored in RTC std::vector_data; diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 85ff50261..6693c5e92 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -634,53 +634,8 @@ void SensorSendTask(struct EventStruct *event, unsigned long timestampUnixTime, TempEvent.timestamp = timestampUnixTime; checkDeviceVTypeForTask(&TempEvent); - - const uint8_t valueCount = getValueCountForTask(event->TaskIndex); - // Store the previous value, in case %pvalue% is used in the formula - String preValue[VARS_PER_TASK]; - const bool processFormula = Device[DeviceIndex].FormulaOption && Cache.hasFormula(event->TaskIndex); - if (processFormula) { - for (uint8_t varNr = 0; varNr < valueCount; varNr++) - { - const String formula = Cache.getTaskDeviceFormula(event->TaskIndex, varNr); - if (!formula.isEmpty()) - { - if (formula.indexOf(F("%pvalue%")) != -1) { - preValue[varNr] = formatUserVarNoCheck(&TempEvent, varNr); - } - } - } - } - - { - String dummy; - success = PluginCall(PLUGIN_READ, &TempEvent, dummy); - } - - if (success) - { - if (processFormula) { - for (uint8_t varNr = 0; varNr < valueCount; varNr++) - { - String formula = Cache.getTaskDeviceFormula(event->TaskIndex, varNr); - if (!formula.isEmpty()) - { - START_TIMER; - - // TD-er: Should we use the set nr of decimals here, or not round at all? - // See: https://github.com/letscontrolit/ESPEasy/issues/3721#issuecomment-889649437 - formula.replace(F("%pvalue%"), preValue[varNr]); - formula.replace(F("%value%"), formatUserVarNoCheck(&TempEvent, varNr)); - ESPEASY_RULES_FLOAT_TYPE result{}; - - if (!isError(Calculate(parseTemplate(formula), result))) { - UserVar.set(event->TaskIndex, varNr, result, TempEvent.sensorType); - } - - STOP_TIMER(COMPUTE_FORMULA_STATS); - } - } - } + String dummy; + if (PluginCall(PLUGIN_READ, &TempEvent, dummy)) { sendData(&TempEvent); } } diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index 012c248ff..e6a05e9a0 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -896,6 +896,7 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) // Each of these may update ExtraTaskSettings, but it may not have been saved yet. // Thus update the cache just in case something from it is requested from the cache. Cache.updateExtraTaskSettingsCache(); + UserVar.clear_computed(event->TaskIndex); } if (Function == PLUGIN_SET_DEFAULTS) { saveUserVarToRTC(); diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 45404df48..1ef649299 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -31,6 +31,7 @@ #include "../Globals/Plugins.h" #include "../Globals/RTC.h" #include "../Globals/ResetFactoryDefaultPref.h" +#include "../Globals/RuntimeData.h" #include "../Globals/SecuritySettings.h" #include "../Globals/Settings.h" #include "../Globals/WiFi_AP_Candidates.h" @@ -1093,6 +1094,7 @@ String SaveTaskSettings(taskIndex_t TaskIndex) err = checkTaskSettings(TaskIndex); } #endif + UserVar.clear_computed(ExtraTaskSettings.TaskIndex); } #ifndef LIMIT_BUILD_SIZE else { @@ -1152,6 +1154,7 @@ String LoadTaskSettings(taskIndex_t TaskIndex) ExtraTaskSettings.validate(); Cache.updateExtraTaskSettingsCache_afterLoad_Save(); + UserVar.clear_computed(ExtraTaskSettings.TaskIndex); STOP_TIMER(LOAD_TASK_SETTINGS); return result; diff --git a/src/src/PluginStructs/P109_data_struct.cpp b/src/src/PluginStructs/P109_data_struct.cpp index 9ac605130..fa921feb7 100644 --- a/src/src/PluginStructs/P109_data_struct.cpp +++ b/src/src/PluginStructs/P109_data_struct.cpp @@ -293,7 +293,7 @@ void P109_data_struct::saveThermoSettings(struct EventStruct *event) { fs::File f = tryOpenFile(fileName, F("w")); if (f) { - f.write(reinterpret_cast(&UserVar[event->BaseVarIndex]), 16); + f.write(reinterpret_cast(UserVar.getTaskValues_Data(event->TaskIndex)), 16); f.close(); flashCount(); } diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 849eea379..a2fc5c848 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -169,6 +169,7 @@ void handle_devices() { // N.B. When calling delete, the settings were already saved. if (nosave) { Cache.updateExtraTaskSettingsCache(); + UserVar.clear_computed(taskIndex); } else { addHtmlError(SaveTaskSettings(taskIndex)); addHtmlError(SaveSettings()); @@ -457,6 +458,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task CPluginCall(ProtocolIndex, CPlugin::Function::CPLUGIN_TASK_CHANGE_NOTIFICATION, &TempEvent, dummy); } } + UserVar.clear_computed(taskIndex); }