mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
Merge pull request #3745 from TD-er/bugfix/format_decimals_formula
[Formula] Use correct nr of decimals in formula (#3721)
This commit is contained in:
@@ -279,3 +279,87 @@ The conversion always outputs a string, but not all of these can be converted ba
|
||||
- Convert a (known) unit number to its IP Address. (Added: 2020/11/08)
|
||||
|
||||
f_opt: for invalid IP: 0 = ``(IP unset)`` 1 = (empty string) 2 = ``0``
|
||||
|
||||
|
||||
Task Formulas
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Most tasks support using formulas.
|
||||
These will be called when a task's ``PLUGIN_READ`` is called.
|
||||
|
||||
The formula can perform basic calculations.
|
||||
In these calculations the new read value can be referred to via ``%value%``.
|
||||
It is also possible to refer to the previous value, from before ``PLUGIN_READ`` is called.
|
||||
This previous value can be referred to via ``%pvalue%``
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. note::
|
||||
Use of "Standard Conversions" and referring other task values in formula was added on 2021-08-06
|
||||
|
||||
|
||||
Convert from Celsius to Fahrenheit
|
||||
""""""""""""""""""""""""""""""""""
|
||||
|
||||
* Using a formula: ``(%value%*9/5)+32``
|
||||
* Using above mentioned "Standard Conversions": ``%c_c2f%(%value%)``
|
||||
|
||||
|
||||
Compute dew point
|
||||
"""""""""""""""""
|
||||
|
||||
In formulas one may also refer to other task values.
|
||||
For example when using a BME280, which can measure temperature and humidity, it could be useful to output the dew point temperature instead of the actual temperature.
|
||||
|
||||
For this conversion, ``%c_dew_th%`` can be used, but it does need 2 input values:
|
||||
|
||||
* Temperature
|
||||
* Humidity
|
||||
|
||||
Let's assume we have a task called "bme" which has a task value named "H" (humidity).
|
||||
To replace the measured temperature with the dew point, one may want to use the following conversion:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
%c_dew_th%(%value%,[bme#H])
|
||||
|
||||
Compute altitude based on air pressure
|
||||
""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
An ESPEasy node may receive sensor data from another remote node.
|
||||
For example a node may have 2 tasks:
|
||||
|
||||
* "local" receiving the air pressure from a sensor
|
||||
* "remote" which has a task value "P" which contains the remote air pressure.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
%c_alt_pres_sea%(%value%,[remote#P])
|
||||
|
||||
With this formula set at the "local" task which measures the air pressure, the unit of measure is converted from air pressure to altitude in meters, compared to the remote sensor.
|
||||
|
||||
This "remote" task may be received via ESPEasy p2p or can be set by the ``TaskValueSet`` command in rules to a dummy task.
|
||||
|
||||
|
||||
|
||||
Finite Impulse Response Filter
|
||||
""""""""""""""""""""""""""""""
|
||||
|
||||
A Finate Impulse Response Filter (FIR) does only add a fraction of the change to the new value.
|
||||
This does dampen the effect of a sudden spike in the readings and just follows the trend of the measured value.
|
||||
|
||||
It can also be used as a simple interpolate function for some values that may flip a number of times between 2 discrete values.
|
||||
For example most A/D converters may flip between 2 discrete levels, where this flipping may be regarded as a duty cycle corresponding to where the actual value may be between both discrete levels of the ADC.
|
||||
|
||||
The factor used in an FIR is a trade-off between strength of filtering and adding a delay to the response time.
|
||||
|
||||
Since formulas only can refer to one previous value, we can only make a FIR filter with order N = 2.
|
||||
|
||||
An example with a weight of 0.25:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
%pvalue% + (%value%-%pvalue%)/4
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ Special Notations
|
||||
* ``{...:...}`` Referring to String conversions
|
||||
* Quotes (single, double or back quotes) Marking begin and end of a command parameter
|
||||
|
||||
.. note::
|
||||
Formulas used in tasks (thus not using the rules) may refer to ``%value%`` for the new current value and ``%pvalue%`` for the previous value before ``PLUGIN_READ`` was called.
|
||||
These notations cannot be used in the rules.
|
||||
If a previous value is needed, one has to use variables for it.
|
||||
|
||||
|
||||
Syntax
|
||||
|
||||
@@ -486,7 +486,7 @@ Show JSON
|
||||
=========
|
||||
|
||||
Show Metrics
|
||||
=========
|
||||
============
|
||||
|
||||
Shows various system metrics and device values in prometheus format
|
||||
|
||||
|
||||
@@ -285,7 +285,7 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String &string)
|
||||
String deviceTemperatureTemplateString(deviceTemperatureTemplate);
|
||||
String pooltempString(parseTemplate(deviceTemperatureTemplateString, 40));
|
||||
|
||||
if (Calculate(pooltempString.c_str(), value) != CalculateReturnCode::OK)
|
||||
if (Calculate(pooltempString, value) != CalculateReturnCode::OK)
|
||||
{
|
||||
addFormNote(F("It seems I can't parse your formulae. Fixed value will be used!"));
|
||||
value = FIXED_TEMP_VALUE;
|
||||
@@ -417,7 +417,7 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String &string)
|
||||
readCommand = F("RT,");
|
||||
double temperatureReading;
|
||||
|
||||
if (Calculate(pooltempString.c_str(), temperatureReading) != CalculateReturnCode::OK)
|
||||
if (Calculate(pooltempString, temperatureReading) != CalculateReturnCode::OK)
|
||||
{
|
||||
temperatureReading = FIXED_TEMP_VALUE;
|
||||
}
|
||||
|
||||
@@ -603,12 +603,21 @@ void SensorSendTask(taskIndex_t TaskIndex)
|
||||
struct EventStruct TempEvent(TaskIndex);
|
||||
checkDeviceVTypeForTask(&TempEvent);
|
||||
|
||||
// TempEvent.idx = Settings.TaskDeviceID[TaskIndex]; todo check
|
||||
|
||||
float preValue[VARS_PER_TASK]; // store values before change, in case we need it in the formula
|
||||
|
||||
for (uint8_t varNr = 0; varNr < VARS_PER_TASK; varNr++) {
|
||||
preValue[varNr] = UserVar[TempEvent.BaseVarIndex + varNr];
|
||||
const uint8_t valueCount = getValueCountForTask(TaskIndex);
|
||||
// Store the previous value, in case %pvalue% is used in the formula
|
||||
String preValue[VARS_PER_TASK];
|
||||
if (Device[DeviceIndex].FormulaOption) {
|
||||
for (uint8_t varNr = 0; varNr < valueCount; varNr++)
|
||||
{
|
||||
if (ExtraTaskSettings.TaskDeviceFormula[varNr][0] != 0)
|
||||
{
|
||||
const String formula = ExtraTaskSettings.TaskDeviceFormula[varNr];
|
||||
if (formula.indexOf(F("%pvalue%")) != -1) {
|
||||
preValue[varNr] = formatUserVarNoCheck(&TempEvent, varNr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Settings.TaskDeviceDataFeed[TaskIndex] == 0) // only read local connected sensorsfeeds
|
||||
@@ -625,16 +634,18 @@ void SensorSendTask(taskIndex_t TaskIndex)
|
||||
if (Device[DeviceIndex].FormulaOption) {
|
||||
START_TIMER;
|
||||
|
||||
for (uint8_t varNr = 0; varNr < VARS_PER_TASK; varNr++)
|
||||
for (uint8_t varNr = 0; varNr < valueCount; varNr++)
|
||||
{
|
||||
if (ExtraTaskSettings.TaskDeviceFormula[varNr][0] != 0)
|
||||
{
|
||||
// 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
|
||||
String formula = ExtraTaskSettings.TaskDeviceFormula[varNr];
|
||||
formula.replace(F("%pvalue%"), String(preValue[varNr]));
|
||||
formula.replace(F("%value%"), String(UserVar[TempEvent.BaseVarIndex + varNr]));
|
||||
formula.replace(F("%pvalue%"), preValue[varNr]);
|
||||
formula.replace(F("%value%"), formatUserVarNoCheck(&TempEvent, varNr));
|
||||
double result = 0;
|
||||
|
||||
if (!isError(Calculate(formula, result))) {
|
||||
if (!isError(Calculate(parseTemplate(formula), result))) {
|
||||
UserVar[TempEvent.BaseVarIndex + varNr] = result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user