mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-12 01:24:04 +00:00
[Rules] Allow transformations on [var#n] and add [int#n]
Also add some caches to find task-value names. Fix some minor issue when referring [var#n] variables. These were always rounded to 2 decimals. Also added [int#n] to address the same variables as with [var#n] but rounded to integer values, to be used in rules for more reliable comparing.
This commit is contained in:
+17
-11
@@ -239,17 +239,17 @@ remember to add them after the code and always begin with "//":
|
||||
endif //this is another comment
|
||||
endon
|
||||
|
||||
Refering values
|
||||
Referring values
|
||||
---------------
|
||||
|
||||
Rules and some plugins can use references to other (dynamic) values within ESPeasy.
|
||||
|
||||
The syntax for refering other values is: ``[...#...]``
|
||||
The syntax for referring other values is: ``[...#...]``
|
||||
Sometimes it can be useful to have some extra options, each separated using a '#' like this: ``[...#...#...]``
|
||||
|
||||
Reference to a value of a specific task: ``[TaskName#ValueName]``
|
||||
|
||||
Refering a value using some pre-defined format: ``[TaskName#ValueName#transformation#justification]``
|
||||
Referring a value using some pre-defined format: ``[TaskName#ValueName#transformation#justification]``
|
||||
|
||||
|
||||
For example, there is a task named "bme280" which has a value named "temperature".
|
||||
@@ -265,36 +265,42 @@ N.B. these references to task values only yield a value when the task is enabled
|
||||
Special task names
|
||||
------------------
|
||||
|
||||
You must not use the task names ``Plugin`` or ``VAR`` as these hae special meaning.
|
||||
You must not use the task names ``Plugin``, ``VAR`` ``INT`` as these hae special meaning.
|
||||
|
||||
``Plugin`` can be used in a so called ``PLUGIN_REQUEST``, for example:
|
||||
``[Plugin#GPIO#Pinstate#N]`` to get the pin state of a GPIO pin.
|
||||
|
||||
``Var`` is used for internal variables.
|
||||
``Var`` and ``INT`` are used for internal variables.
|
||||
The variables set with the ``Let`` command will be available in rules
|
||||
as ``VAR#N`` where ``N`` is 1..16.
|
||||
as ``VAR#N`` or ``INT#N`` where ``N`` is 1..16.
|
||||
For example: ``Let,10,[VAR#9]``
|
||||
|
||||
Clock, Rules and System etc. are not recommended either since they are used in
|
||||
N.B. ``INT`` and ``VAR`` use the same variable, only ``INT`` does round them to 0 decimals.
|
||||
N.B.2 ``INT`` is added in build 20190916.
|
||||
|
||||
``Clock``, ``Rules`` and ``System`` etc. are not recommended either since they are used in
|
||||
event names.
|
||||
|
||||
Please observe that task names are case insensitive meaning that VAR, var, and Var etc.
|
||||
are all treated the same.
|
||||
|
||||
|
||||
Formatting refered values
|
||||
-------------------------
|
||||
|
||||
When refering another value, some basic formatting can be used.
|
||||
When referring another value, some basic formatting can be used.
|
||||
|
||||
Refering a value using some pre-defined format: ``[TaskName#ValueName#transformation#justification]``
|
||||
Referring a value using some pre-defined format: ``[TaskName#ValueName#transformation#justification]``
|
||||
|
||||
Transformation
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
* Transformations are case sensitive. (``M`` differs from ``m``, capital is more verbose)
|
||||
* Most transformations work on "binary" values (0 or 1)
|
||||
* Transformations can not be used on "Plugin" calls, like ``[Plugin#GPIO#Pinstate#N]``, since these already use multiple occurences of ``#``.
|
||||
* Most transformations work on "binary" values (logic values 0 or 1)
|
||||
* A "binary" transformation can be "inverted" by adding a leading ``!``.
|
||||
* A "binary" value is considered 0 when its string value is "0", otherwise it is an 1. (best to round a value to 0 decimals for this)
|
||||
* A "binary" value is considered 0 when its string value is "0" or empty, otherwise it is an 1. (float values are rounded)
|
||||
* A "binary" value can also be used to detect presence of a string, as it is 0 on an empty string or 1 otherwise.
|
||||
|
||||
Binary transformations:
|
||||
|
||||
|
||||
@@ -197,6 +197,7 @@ void check_size() {
|
||||
#include "DataStructs/SystemTimerStruct.h"
|
||||
#include "DataStructs/RTCStruct.h"
|
||||
#include "DataStructs/PortStatusStruct.h"
|
||||
#include "DataStructs/Caches.h"
|
||||
|
||||
|
||||
CRCStruct CRCValues;
|
||||
@@ -208,6 +209,7 @@ LogStruct Logging;
|
||||
NotificationStruct Notification[NPLUGIN_MAX];
|
||||
RTCStruct RTC;
|
||||
DeviceVector Device;
|
||||
Caches Cache;
|
||||
|
||||
std::map<int, TimingStats> pluginStats;
|
||||
std::map<int, TimingStats> controllerStats;
|
||||
|
||||
+89
-54
@@ -156,42 +156,83 @@ String rulesProcessingFile(const String& fileName, String& event) {
|
||||
fs::File f = tryOpenFile(fileName, "r+");
|
||||
SPIFFS_CHECK(f, fileName.c_str());
|
||||
|
||||
String line = "";
|
||||
bool match = false;
|
||||
bool codeBlock = false;
|
||||
bool isCommand = false;
|
||||
bool condition[RULES_IF_MAX_NESTING_LEVEL];
|
||||
bool ifBranche[RULES_IF_MAX_NESTING_LEVEL];
|
||||
byte ifBlock = 0;
|
||||
byte fakeIfBlock = 0;
|
||||
String line;
|
||||
line.reserve(RULES_IF_MAX_NESTING_LEVEL);
|
||||
bool match = false;
|
||||
bool codeBlock = false;
|
||||
bool isCommand = false;
|
||||
bool condition[RULES_IF_MAX_NESTING_LEVEL];
|
||||
bool ifBranche[RULES_IF_MAX_NESTING_LEVEL];
|
||||
byte ifBlock = 0;
|
||||
byte fakeIfBlock = 0;
|
||||
|
||||
byte *buf = new byte[RULES_BUFFER_SIZE]();
|
||||
std::vector<byte> buf;
|
||||
buf.resize(RULES_BUFFER_SIZE);
|
||||
|
||||
bool firstNonSpaceRead = false;
|
||||
bool commentFound = false;
|
||||
|
||||
while (f.available()) {
|
||||
int len = f.read((byte *)buf, RULES_BUFFER_SIZE);
|
||||
int len = f.read(&buf[0], RULES_BUFFER_SIZE);
|
||||
|
||||
for (int x = 0; x < len; x++) {
|
||||
int data = buf[x];
|
||||
|
||||
SPIFFS_CHECK(data >= 0, fileName.c_str());
|
||||
|
||||
if (data != 10) {
|
||||
line += char(data);
|
||||
} else { // if line complete, parse this rule
|
||||
line.replace("\r", "");
|
||||
switch (static_cast<char>(data))
|
||||
{
|
||||
case 10: // "\n"
|
||||
{
|
||||
// Line end, parse rule
|
||||
if (!line.startsWith(F("//")) && (line.length() > 0)) {
|
||||
parseCompleteNonCommentLine(line, event, log, match, codeBlock,
|
||||
isCommand, condition, ifBranche, ifBlock,
|
||||
fakeIfBlock);
|
||||
backgroundtasks();
|
||||
}
|
||||
|
||||
if ((line.substring(0, 2) != F("//")) && (line.length() > 0)) {
|
||||
parseCompleteNonCommentLine(line, event, log, match, codeBlock,
|
||||
isCommand, condition, ifBranche, ifBlock,
|
||||
fakeIfBlock);
|
||||
backgroundtasks();
|
||||
// Prepare for new line
|
||||
line = "";
|
||||
firstNonSpaceRead = false;
|
||||
commentFound = false;
|
||||
break;
|
||||
}
|
||||
case 13: // "\r", Just skip this character
|
||||
break;
|
||||
case '\t': // tab
|
||||
case 32: // space
|
||||
{
|
||||
// Strip leading spaces.
|
||||
if (firstNonSpaceRead) {
|
||||
line += ' ';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '/':
|
||||
{
|
||||
if (!commentFound) {
|
||||
line += '/';
|
||||
|
||||
line = "";
|
||||
if (line.endsWith("//")) {
|
||||
// consider the rest of the line a comment
|
||||
commentFound = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: // Any other character
|
||||
{
|
||||
firstNonSpaceRead = true;
|
||||
|
||||
if (!commentFound) {
|
||||
line += char(data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] buf;
|
||||
|
||||
if (f) {
|
||||
f.close();
|
||||
@@ -202,6 +243,25 @@ String rulesProcessingFile(const String& fileName, String& event) {
|
||||
return "";
|
||||
}
|
||||
|
||||
void replace_EventValueN_Argv(String& line, const String& argString, unsigned int argc)
|
||||
{
|
||||
String eventvalue;
|
||||
|
||||
eventvalue.reserve(16);
|
||||
eventvalue = F("%eventvalue");
|
||||
eventvalue += argc;
|
||||
eventvalue += '%';
|
||||
String tmpParam;
|
||||
|
||||
if (GetArgv(argString.c_str(), tmpParam, argc)) {
|
||||
if (argc == 1) {
|
||||
// For compatibility reasons also replace %eventvalue%
|
||||
line.replace(F("%eventvalue%"), tmpParam);
|
||||
}
|
||||
line.replace(eventvalue, tmpParam);
|
||||
}
|
||||
}
|
||||
|
||||
void parseCompleteNonCommentLine(String& line, String& event, String& log,
|
||||
bool& match, bool& codeBlock, bool& isCommand,
|
||||
bool condition[], bool ifBranche[],
|
||||
@@ -220,7 +280,8 @@ void parseCompleteNonCommentLine(String& line, String& event, String& log,
|
||||
// "on" (no codeBlock)
|
||||
// This to avoid waisting CPU time...
|
||||
|
||||
if (match && !fakeIfBlock) {
|
||||
// Only process the %eventvalueX% replacements if there is any present.
|
||||
if (match && !fakeIfBlock && (line.indexOf(F("%eventvalue")) != -1)) {
|
||||
// substitution of %eventvalue% is made here so it can be used on if
|
||||
// statement too
|
||||
if (event.charAt(0) == '!') {
|
||||
@@ -231,38 +292,12 @@ void parseCompleteNonCommentLine(String& line, String& event, String& log,
|
||||
int equalsPos = event.indexOf("=");
|
||||
|
||||
if (equalsPos > 0) {
|
||||
String tmpString = event.substring(equalsPos + 1);
|
||||
// Replace %eventvalueX% with the actual value of the event.
|
||||
String argString = event.substring(equalsPos + 1);
|
||||
|
||||
// line.replace(F("%eventvalue%"), tmpString); // substitute
|
||||
// %eventvalue% with the actual value from the event
|
||||
String tmpParam;
|
||||
|
||||
if (GetArgv(tmpString.c_str(), tmpParam, 1)) {
|
||||
line.replace(F("%eventvalue%"),
|
||||
tmpParam); // for compatibility issues
|
||||
line.replace(F("%eventvalue1%"),
|
||||
tmpParam); // substitute %eventvalue1% in actions with
|
||||
// the actual value from the event
|
||||
for (unsigned int argc = 1; argc <= 4; ++argc) {
|
||||
replace_EventValueN_Argv(line, argString, argc);
|
||||
}
|
||||
|
||||
if (GetArgv(tmpString.c_str(), tmpParam, 2)) {
|
||||
line.replace(F("%eventvalue2%"),
|
||||
tmpParam); // substitute %eventvalue2% in actions with
|
||||
}
|
||||
|
||||
// the actual value from the event
|
||||
if (GetArgv(tmpString.c_str(), tmpParam, 3)) {
|
||||
line.replace(F("%eventvalue3%"),
|
||||
tmpParam); // substitute %eventvalue3% in actions with
|
||||
}
|
||||
|
||||
// the actual value from the event
|
||||
if (GetArgv(tmpString.c_str(), tmpParam, 4)) {
|
||||
line.replace(F("%eventvalue4%"),
|
||||
tmpParam); // substitute %eventvalue4% in actions with
|
||||
}
|
||||
|
||||
// the actual value from the event
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -853,7 +888,7 @@ void createRuleEvents(struct EventStruct *event) {
|
||||
for (byte varNr = 0; varNr < Device[DeviceIndex].ValueCount; varNr++) {
|
||||
String eventString;
|
||||
eventString.reserve(32); // Enough for most use cases, prevent lots of memory allocations.
|
||||
eventString = getTaskDeviceName(event->TaskIndex);
|
||||
eventString = getTaskDeviceName(event->TaskIndex);
|
||||
eventString += F("#");
|
||||
eventString += ExtraTaskSettings.TaskDeviceValueNames[varNr];
|
||||
eventString += F("=");
|
||||
@@ -865,7 +900,7 @@ void createRuleEvents(struct EventStruct *event) {
|
||||
break;
|
||||
case SENSOR_TYPE_STRING:
|
||||
|
||||
// FIXME TD-er: What to add here? length of string?
|
||||
// FIXME TD-er: What to add here? length of string?
|
||||
break;
|
||||
default:
|
||||
|
||||
|
||||
@@ -788,6 +788,7 @@ String SaveToFile(char *fname, int index, byte *memAddress, int datasize)
|
||||
fs::File f = tryOpenFile(fname, "r+");
|
||||
|
||||
if (f) {
|
||||
Cache.clearAllCaches();
|
||||
SPIFFS_CHECK(f, fname);
|
||||
SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname);
|
||||
byte *pointerToByteToSave = memAddress;
|
||||
|
||||
+80
-60
@@ -460,9 +460,9 @@ boolean remoteConfig(struct EventStruct *event, const String& string)
|
||||
if ((configTaskName.length() == 0) || (configCommand.length() == 0)) {
|
||||
return success; // TD-er: Should this be return false?
|
||||
}
|
||||
int8_t index = getTaskIndexByName(configTaskName);
|
||||
byte index = findTaskIndexByName(configTaskName);
|
||||
|
||||
if (index != -1)
|
||||
if (index != TASKS_MAX)
|
||||
{
|
||||
event->TaskIndex = index;
|
||||
success = PluginCall(PLUGIN_SET_CONFIG, event, configCommand);
|
||||
@@ -472,21 +472,6 @@ boolean remoteConfig(struct EventStruct *event, const String& string)
|
||||
return success;
|
||||
}
|
||||
|
||||
int8_t getTaskIndexByName(const String& TaskNameSearch)
|
||||
{
|
||||
for (byte x = 0; x < TASKS_MAX; x++)
|
||||
{
|
||||
LoadTaskSettings(x);
|
||||
String TaskName = getTaskDeviceName(x);
|
||||
|
||||
if ((TaskName.length() != 0) && (TaskNameSearch.equalsIgnoreCase(TaskName)))
|
||||
{
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
Device GPIO name functions to share flash strings
|
||||
\*********************************************************************************************/
|
||||
@@ -828,6 +813,7 @@ String checkTaskSettings(byte taskIndex) {
|
||||
return F("Warning: Task Device Name is empty. It is adviced to give tasks an unique name");
|
||||
}
|
||||
}
|
||||
// Do not use the cached function findTaskIndexByName since that one does rely on the fact names should be unique.
|
||||
for (int i = 0; i < TASKS_MAX; ++i) {
|
||||
if (i != taskIndex && Settings.TaskDeviceEnabled[i]) {
|
||||
LoadTaskSettings(i);
|
||||
@@ -1468,7 +1454,7 @@ String parseTemplate(String& tmpString, byte lineSize)
|
||||
while (findNextDevValNameInString(tmpString, startpos, endpos, deviceName, valueName, format)) {
|
||||
// First copy all upto the start of the [...#...] part to be replaced.
|
||||
newString += tmpString.substring(lastStartpos, startpos);
|
||||
|
||||
|
||||
if (deviceName.equalsIgnoreCase(F("Plugin")))
|
||||
{
|
||||
// Handle a plugin request.
|
||||
@@ -1483,18 +1469,29 @@ String parseTemplate(String& tmpString, byte lineSize)
|
||||
|
||||
if (PluginCall(PLUGIN_REQUEST, 0, command))
|
||||
{
|
||||
// Do not call transformValue here.
|
||||
// The "format" is not empty so must not call the formatter function.
|
||||
newString += command;
|
||||
}
|
||||
}
|
||||
else if (deviceName.equalsIgnoreCase(F("Var")))
|
||||
else if (deviceName.equalsIgnoreCase(F("Var")) || deviceName.equalsIgnoreCase(F("Int")))
|
||||
{
|
||||
// Address an internal variable
|
||||
// Address an internal variable either as float or as int
|
||||
// For example: Let,10,[VAR#9]
|
||||
int varNum;
|
||||
|
||||
if (validIntFromString(valueName, varNum)) {
|
||||
if ((varNum > 0) && (varNum <= CUSTOM_VARS_MAX)) {
|
||||
newString += String(customFloatVar[varNum - 1]);
|
||||
unsigned char nr_decimals = 2;
|
||||
if (deviceName.equalsIgnoreCase(F("Int"))) {
|
||||
nr_decimals = 0;
|
||||
} else if (format.length() != 0)
|
||||
{
|
||||
// There is some formatting here, so do not throw away decimals
|
||||
nr_decimals = 6;
|
||||
}
|
||||
String value = String(customFloatVar[varNum - 1], nr_decimals);
|
||||
transformValue(newString, lineSize, value, format, tmpString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1506,7 +1503,7 @@ String parseTemplate(String& tmpString, byte lineSize)
|
||||
// For example: "[<taskname>#getLevel]"
|
||||
byte taskIndex = findTaskIndexByName(deviceName);
|
||||
|
||||
if (taskIndex != TASKS_MAX) {
|
||||
if (taskIndex != TASKS_MAX && Settings.TaskDeviceEnabled[taskIndex]) {
|
||||
byte valueNr = findDeviceValueIndexByName(valueName, taskIndex);
|
||||
|
||||
if (valueNr != VARS_PER_TASK) {
|
||||
@@ -1526,11 +1523,12 @@ String parseTemplate(String& tmpString, byte lineSize)
|
||||
|
||||
if (PluginCall(PLUGIN_GET_CONFIG, &TempEvent, tmpName))
|
||||
{
|
||||
newString += tmpName;
|
||||
transformValue(newString, lineSize, tmpName, format, tmpString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Conversion is done (or impossible) for the found "[...#...]"
|
||||
// Continue with the next one.
|
||||
@@ -1562,24 +1560,26 @@ String parseTemplate(String& tmpString, byte lineSize)
|
||||
return newString;
|
||||
}
|
||||
|
||||
// Find the first enabled task with given name
|
||||
// Find the first task with given name
|
||||
// Return TASKS_MAX when not found, else return taskIndex
|
||||
byte findTaskIndexByName(const String& deviceName)
|
||||
{
|
||||
// FIXME TD-er: Should cache this.
|
||||
// cache this, since LoadTaskSettings does take some time.
|
||||
auto result = Cache.taskIndexName.find(deviceName);
|
||||
if (result != Cache.taskIndexName.end()) {
|
||||
return result->second;
|
||||
}
|
||||
for (byte taskIndex = 0; taskIndex < TASKS_MAX; taskIndex++)
|
||||
{
|
||||
if (Settings.TaskDeviceEnabled[taskIndex])
|
||||
{
|
||||
LoadTaskSettings(taskIndex);
|
||||
String taskDeviceName = getTaskDeviceName(taskIndex);
|
||||
LoadTaskSettings(taskIndex);
|
||||
String taskDeviceName = getTaskDeviceName(taskIndex);
|
||||
|
||||
if (taskDeviceName.length() != 0)
|
||||
if (taskDeviceName.length() != 0)
|
||||
{
|
||||
if (deviceName.equalsIgnoreCase(taskDeviceName))
|
||||
{
|
||||
if (deviceName.equalsIgnoreCase(taskDeviceName))
|
||||
{
|
||||
return taskIndex;
|
||||
}
|
||||
Cache.taskIndexName[deviceName] = taskIndex;
|
||||
return taskIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1590,13 +1590,18 @@ byte findTaskIndexByName(const String& deviceName)
|
||||
// Return VARS_PER_TASK if none found.
|
||||
byte findDeviceValueIndexByName(const String& valueName, byte taskIndex)
|
||||
{
|
||||
// FIXME TD-er: Should cache this.
|
||||
// cache this, since LoadTaskSettings does take some time.
|
||||
auto result = Cache.taskIndexValueName.find(valueName);
|
||||
if (result != Cache.taskIndexValueName.end()) {
|
||||
return result->second;
|
||||
}
|
||||
LoadTaskSettings(taskIndex); // Probably already loaded, but just to be sure
|
||||
|
||||
for (byte valueNr = 0; valueNr < VARS_PER_TASK; valueNr++)
|
||||
{
|
||||
if (valueName.equalsIgnoreCase(ExtraTaskSettings.TaskDeviceValueNames[valueNr]))
|
||||
{
|
||||
Cache.taskIndexValueName[valueName] = valueNr;
|
||||
return valueNr;
|
||||
}
|
||||
}
|
||||
@@ -1677,22 +1682,34 @@ void transformValue(
|
||||
// valueJust="justification"
|
||||
if (valueFormat.length() > 0) //do the checks only if a Format is defined to optimize loop
|
||||
{
|
||||
const int val = value == "0" ? 0 : 1; //to be used for GPIO status (0 or 1)
|
||||
const float valFloat = value.toFloat();
|
||||
|
||||
int logicVal = 0;
|
||||
float valFloat = 0.0;
|
||||
if (validFloatFromString(value, valFloat))
|
||||
{
|
||||
//to be used for binary values (0 or 1)
|
||||
logicVal = static_cast<int>(roundf(valFloat)) == 0 ? 0 : 1;
|
||||
} else {
|
||||
if (value.length() > 0) {
|
||||
logicVal = 1;
|
||||
}
|
||||
}
|
||||
String tempValueFormat = valueFormat;
|
||||
int tempValueFormatLength = tempValueFormat.length();
|
||||
const int invertedIndex = tempValueFormat.indexOf('!');
|
||||
const int inverted = invertedIndex >= 0 ? 1 : 0;
|
||||
if (inverted != 0)
|
||||
tempValueFormat.remove(invertedIndex,1);
|
||||
{
|
||||
const int invertedIndex = tempValueFormat.indexOf('!');
|
||||
if (invertedIndex != -1) {
|
||||
// We must invert the value.
|
||||
logicVal = (logicVal == 0) ? 1 : 0;
|
||||
// Remove the '!' from the string.
|
||||
tempValueFormat.remove(invertedIndex,1);
|
||||
}
|
||||
}
|
||||
|
||||
const int rightJustifyIndex = tempValueFormat.indexOf('R');
|
||||
const bool rightJustify = rightJustifyIndex >= 0 ? 1 : 0;
|
||||
if (rightJustify)
|
||||
tempValueFormat.remove(rightJustifyIndex,1);
|
||||
|
||||
tempValueFormatLength = tempValueFormat.length(); //needed because could have been changed after '!' and 'R' removal
|
||||
const int tempValueFormatLength = tempValueFormat.length();
|
||||
|
||||
//Check Transformation syntax
|
||||
if (tempValueFormatLength > 0)
|
||||
@@ -1702,40 +1719,40 @@ void transformValue(
|
||||
case 'V': //value = value without transformations
|
||||
break;
|
||||
case 'O':
|
||||
value = val == inverted ? F("OFF") : F(" ON"); //(equivalent to XOR operator)
|
||||
value = logicVal == 0 ? F("OFF") : F(" ON"); //(equivalent to XOR operator)
|
||||
break;
|
||||
case 'C':
|
||||
value = val == inverted ? F("CLOSE") : F(" OPEN");
|
||||
value = logicVal == 0 ? F("CLOSE") : F(" OPEN");
|
||||
break;
|
||||
case 'M':
|
||||
value = val == inverted ? F("AUTO") : F(" MAN");
|
||||
value = logicVal == 0 ? F("AUTO") : F(" MAN");
|
||||
break;
|
||||
case 'm':
|
||||
value = val == inverted ? F("A") : F("M");
|
||||
value = logicVal == 0 ? F("A") : F("M");
|
||||
break;
|
||||
case 'H':
|
||||
value = val == inverted ? F("COLD") : F(" HOT");
|
||||
value = logicVal == 0 ? F("COLD") : F(" HOT");
|
||||
break;
|
||||
case 'U':
|
||||
value = val == inverted ? F("DOWN") : F(" UP");
|
||||
value = logicVal == 0 ? F("DOWN") : F(" UP");
|
||||
break;
|
||||
case 'u':
|
||||
value = val == inverted ? F("D") : F("U");
|
||||
value = logicVal == 0 ? F("D") : F("U");
|
||||
break;
|
||||
case 'Y':
|
||||
value = val == inverted ? F(" NO") : F("YES");
|
||||
value = logicVal == 0 ? F(" NO") : F("YES");
|
||||
break;
|
||||
case 'y':
|
||||
value = val == inverted ? F("N") : F("Y");
|
||||
value = logicVal == 0 ? F("N") : F("Y");
|
||||
break;
|
||||
case 'X':
|
||||
value = val == inverted ? F("O") : F("X");
|
||||
value = logicVal == 0 ? F("O") : F("X");
|
||||
break;
|
||||
case 'I':
|
||||
value = val == inverted ? F("OUT") : F(" IN");
|
||||
value = logicVal == 0 ? F("OUT") : F(" IN");
|
||||
break;
|
||||
case 'Z' :// return "0" or "1"
|
||||
value = val == inverted ? "0" : "1";
|
||||
value = logicVal == 0 ? "0" : "1";
|
||||
break;
|
||||
case 'D' ://Dx.y min 'x' digits zero filled & 'y' decimal fixed digits
|
||||
{
|
||||
@@ -1770,10 +1787,13 @@ void transformValue(
|
||||
break;
|
||||
}
|
||||
value = toString(valFloat,y);
|
||||
int indexDot;
|
||||
indexDot = value.indexOf('.') > 0 ? value.indexOf('.') : value.length();
|
||||
for (byte f = 0; f < (x - indexDot); f++)
|
||||
int indexDot = value.indexOf('.');
|
||||
if (indexDot == -1) {
|
||||
indexDot = value.length();
|
||||
}
|
||||
for (byte f = 0; f < (x - indexDot); f++) {
|
||||
value = "0" + value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'F' :// FLOOR (round down)
|
||||
@@ -1874,7 +1894,7 @@ void transformValue(
|
||||
}
|
||||
//end of changes by giig1967g - 2018-04-18
|
||||
|
||||
newString += String(value);
|
||||
newString += value;
|
||||
{
|
||||
#ifndef BUILD_NO_DEBUG
|
||||
if (loglevelActiveFor(LOG_LEVEL_DEBUG_DEV)) {
|
||||
|
||||
Reference in New Issue
Block a user