Merge pull request #5574 from tonhuisman/bugfix/TaskValue-update-custom-format-uservar

[Bugfix] Include PLUGIN_FORMAT_USERVAR values for json updates
This commit is contained in:
TD-er
2026-07-12 14:56:43 +02:00
committed by GitHub
6 changed files with 100 additions and 77 deletions
+13 -34
View File
@@ -7,6 +7,7 @@
// ####################################################################################################### // #######################################################################################################
/** Changelog: /** Changelog:
* 2026-06-30 tonhuisman: Code optimization by moving ul2stringFixed to StringConverter_Numerical
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Shift registers) * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Shift registers)
* 2022-02-27 tonhuisman: Rename plugin title to Output - Shift registers (74HC595) * 2022-02-27 tonhuisman: Rename plugin title to Output - Shift registers (74HC595)
* 2022-02-25 tonhuisman: Again rename commands, now using separate prefix shiftout and the rest of the previous command as subcommand. * 2022-02-25 tonhuisman: Again rename commands, now using separate prefix shiftout and the rest of the previous command as subcommand.
@@ -59,19 +60,6 @@
# include "./src/PluginStructs/P126_data_struct.h" # include "./src/PluginStructs/P126_data_struct.h"
// TODO tonhuisman: ? Move to StringConverter ? though it is a bit specific, can also be used by P129
String P126_ul2stringFixed(uint32_t value, uint8_t base) {
uint64_t val = static_cast<uint64_t>(value);
val &= 0x0ffffffff; // Keep 32 bits
val |= 0x100000000; // Set bit just left of 32 bits so we will see the leading zeroes
String valStr = ull2String(val, base);
valStr.remove(0, 1); // Delete leading 1 we added
valStr.toUpperCase(); // uppercase hex for readability
return valStr;
}
boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string) boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string)
{ {
boolean success = false; boolean success = false;
@@ -286,15 +274,16 @@ boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string)
(P126_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P126_OUTPUT_HEXBIN)) { (P126_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P126_OUTPUT_HEXBIN)) {
string += '0'; string += '0';
string += (P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? 'b' : 'x'); string += (P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? 'b' : 'x');
string += P126_ul2stringFixed(UserVar.getUint32(event->TaskIndex, event->idx),
# ifdef P126_SHOW_VALUES string += ul2stringFixed(UserVar.getUint32(event->TaskIndex, event->idx),
(P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : # ifdef P126_SHOW_VALUES
# endif // ifdef P126_SHOW_VALUES (P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN :
HEX # endif // ifdef P126_SHOW_VALUES
# ifdef P126_SHOW_VALUES HEX
) # ifdef P126_SHOW_VALUES
# endif // ifdef P126_SHOW_VALUES )
); # endif // ifdef P126_SHOW_VALUES
, 1 == event->ParN[event->idx]);
} }
success = true; success = true;
break; break;
@@ -308,21 +297,15 @@ boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string)
const String abcd = F("ABCDEFGH"); // In case anyone dares to extend const String abcd = F("ABCDEFGH"); // In case anyone dares to extend
// VARS_PER_TASK to 8... // VARS_PER_TASK to 8...
const uint16_t endCheck = P126_CONFIG_CHIP_COUNT + (P126_CONFIG_CHIP_COUNT == 255 ? 3 : 4); // 4(.0) = nr of bytes in an uint32_t. const uint16_t endCheck = P126_CONFIG_CHIP_COUNT + (P126_CONFIG_CHIP_COUNT == 255 ? 3 : 4); // 4(.0) = nr of bytes in an uint32_t.
const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK), static_cast<uint8_t>(ceil(P126_CONFIG_CHIP_COUNT / 4.0))); const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK), static_cast<uint8_t>(ceilf(P126_CONFIG_CHIP_COUNT / 4.0f)));
uint8_t dotInsert;
uint8_t dotOffset;
for (uint16_t varNr = 0; varNr < maxVar; ++varNr) { for (uint16_t varNr = 0; varNr < maxVar; ++varNr) {
if (P126_CONFIG_FLAGS_GET_VALUES_DISPLAY) { if (P126_CONFIG_FLAGS_GET_VALUES_DISPLAY) {
label = F("Bin"); label = F("Bin");
state = F("0b"); state = F("0b");
dotInsert = 10;
dotOffset = 9;
} else { } else {
label = F("Hex"); label = F("Hex");
state = F("0x"); state = F("0x");
dotInsert = 4;
dotOffset = 3;
} }
label += strformat(F(" State_%s "), abcd.substring(varNr, varNr + 1).c_str()); label += strformat(F(" State_%s "), abcd.substring(varNr, varNr + 1).c_str());
@@ -331,11 +314,7 @@ boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string)
label += (P126_CONFIG_SHOW_OFFSET + (4 * varNr) + 1); // 4 = nr of bytes in an uint32_t. label += (P126_CONFIG_SHOW_OFFSET + (4 * varNr) + 1); // 4 = nr of bytes in an uint32_t.
if ((P126_CONFIG_SHOW_OFFSET + (4 * varNr) + 4) <= endCheck) { // Only show if still in range if ((P126_CONFIG_SHOW_OFFSET + (4 * varNr) + 4) <= endCheck) { // Only show if still in range
state += P126_ul2stringFixed(UserVar.getUint32(event->TaskIndex, varNr), P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : HEX); state += ul2stringFixed(UserVar.getUint32(event->TaskIndex, varNr), P126_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : HEX, true);
for (uint8_t i = 0; i < 3; ++i, dotInsert += dotOffset) { // Insert readability separators
state = state.substring(0, dotInsert) + '.' + state.substring(dotInsert);
}
pluginWebformShowValue(event->TaskIndex, VARS_PER_TASK + varNr, label, state, true); pluginWebformShowValue(event->TaskIndex, VARS_PER_TASK + varNr, label, state, true);
} }
} }
+22 -42
View File
@@ -7,6 +7,7 @@
// ####################################################################################################### // #######################################################################################################
/** Changelog: /** Changelog:
* 2026-06-30 tonhuisman: Code optimization by moving ul2stringFixed to StringConverter_Numerical
* 2025-06-14 tonhuisman: Add support for Custom Value Type per task value * 2025-06-14 tonhuisman: Add support for Custom Value Type per task value
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Shift registers) * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Shift registers)
* 2023-01-04 tonhuisman: Use DIRECT_pin GPIO functions for faster GPIO handling (mostly on ESP32), string optimization * 2023-01-04 tonhuisman: Use DIRECT_pin GPIO functions for faster GPIO handling (mostly on ESP32), string optimization
@@ -43,19 +44,6 @@
# include "./src/PluginStructs/P129_data_struct.h" # include "./src/PluginStructs/P129_data_struct.h"
// TODO tonhuisman: ? Move to StringConverter ? though it is a bit specific, can also be used by P126
String P129_ul2stringFixed(uint32_t value, uint8_t base) {
// Set bit just left of 32 bits so we will see the leading zeroes
const uint64_t val = static_cast<uint64_t>(value) | 0x100000000ull;
String valStr = ull2String(val, base).substring(1); // Delete leading 1 we added
if (base == HEX) {
valStr.toUpperCase(); // uppercase hex for readability
}
return valStr;
}
boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
{ {
boolean success = false; boolean success = false;
@@ -146,7 +134,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
case PLUGIN_GET_DEVICEVALUECOUNT: case PLUGIN_GET_DEVICEVALUECOUNT:
{ {
event->Par1 = min(static_cast<uint8_t>(VARS_PER_TASK), event->Par1 = min(static_cast<uint8_t>(VARS_PER_TASK),
static_cast<uint8_t>(ceil(P129_CONFIG_CHIP_COUNT / 4.0f))); static_cast<uint8_t>(ceilf(P129_CONFIG_CHIP_COUNT / 4.0f)));
success = true; success = true;
break; break;
} }
@@ -155,7 +143,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
{ {
event->sensorType = static_cast<Sensor_VType>( event->sensorType = static_cast<Sensor_VType>(
min(static_cast<uint8_t>(VARS_PER_TASK), min(static_cast<uint8_t>(VARS_PER_TASK),
static_cast<uint8_t>(ceil(P129_CONFIG_CHIP_COUNT / 4.0f)))); static_cast<uint8_t>(ceilf(P129_CONFIG_CHIP_COUNT / 4.0f))));
event->idx = 0; event->idx = 0;
success = true; success = true;
break; break;
@@ -187,6 +175,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
F("Number of chips (Q7 &rarr; DS)"), F("chipcnt"), P129_CONFIG_CHIP_COUNT); F("Number of chips (Q7 &rarr; DS)"), F("chipcnt"), P129_CONFIG_CHIP_COUNT);
addUnit(concat(F("Daisychained 1.."), P129_MAX_CHIP_COUNT)); addUnit(concat(F("Daisychained 1.."), P129_MAX_CHIP_COUNT));
# ifndef LIMIT_BUILD_SIZE # ifndef LIMIT_BUILD_SIZE
// addFormNote(F("Changing the number of chips will reload the page and update the Event configuration.")); // addFormNote(F("Changing the number of chips will reload the page and update the Event configuration."));
# endif // ifndef LIMIT_BUILD_SIZE # endif // ifndef LIMIT_BUILD_SIZE
} }
@@ -240,12 +229,12 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
if (i % 4 == 0) { if (i % 4 == 0) {
bits = PCONFIG_ULONG(i / 4) & 0x0ffffffff; bits = PCONFIG_ULONG(i / 4) & 0x0ffffffff;
off = 0; off = 0;
# ifndef P129_DEBUG_LOG # ifdef P129_DEBUG_LOG
if (loglevelActiveFor(LOG_LEVEL_INFO)) { if (loglevelActiveFor(LOG_LEVEL_INFO)) {
addLog(LOG_LEVEL_INFO, strformat(F("74HC165 Reading from: %d, bits: %s"), i / 4, P129_ul2stringFixed(bits, BIN).c_str())); addLog(LOG_LEVEL_INFO, strformat(F("74HC165 Reading from: %d, bits: %s"), i / 4, ul2stringFixed(bits, BIN, false).c_str()));
} }
# endif // ifndef P129_DEBUG_LOG # endif // ifdef P129_DEBUG_LOG
} }
html_TR(); html_TR();
addHtml(F("<td align =\"center\">")); addHtml(F("<td align =\"center\">"));
@@ -310,15 +299,15 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
} }
PCONFIG_ULONG(i / 4) = bits; PCONFIG_ULONG(i / 4) = bits;
# ifndef P129_DEBUG_LOG # ifdef P129_DEBUG_LOG
if (loglevelActiveFor(LOG_LEVEL_INFO) && ((i % 4 == 3) || (i == P129_CONFIG_CHIP_COUNT))) { if (loglevelActiveFor(LOG_LEVEL_INFO) && ((i % 4 == 3) || (i == P129_CONFIG_CHIP_COUNT))) {
addLog(LOG_LEVEL_INFO, strformat(F("74HC165 Writing to: %d, offset: %d, bits: %s"), addLog(LOG_LEVEL_INFO, strformat(F("74HC165 Writing to: %d, offset: %d, bits: %s"),
i / 4, i / 4,
off * 8, off * 8,
P129_ul2stringFixed(bits, BIN).c_str())); ul2stringFixed(bits, BIN, false).c_str()));
} }
# endif // ifndef P129_DEBUG_LOG # endif // ifdef P129_DEBUG_LOG
off++; off++;
} }
success = true; success = true;
@@ -391,15 +380,16 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
(P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P129_OUTPUT_HEXBIN)) { (P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION == P129_OUTPUT_HEXBIN)) {
string += '0'; string += '0';
string += (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? 'b' : 'x'); string += (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? 'b' : 'x');
string += P129_ul2stringFixed(UserVar.getUint32(event->TaskIndex, event->idx),
# ifdef P129_SHOW_VALUES string += ul2stringFixed(UserVar.getUint32(event->TaskIndex, event->idx),
(P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : # ifdef P129_SHOW_VALUES
# endif // ifdef P129_SHOW_VALUES (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN :
HEX # endif // ifdef P129_SHOW_VALUES
# ifdef P129_SHOW_VALUES HEX
) # ifdef P129_SHOW_VALUES
# endif // ifdef P129_SHOW_VALUES )
); # endif // ifdef P129_SHOW_VALUES
, 1 == event->ParN[event->idx]);
} }
success = true; success = true;
break; break;
@@ -412,21 +402,15 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
state.reserve(40); state.reserve(40);
const String abcd = F("ABCDEFGH"); // In case anyone dares to extend VARS_PER_TASK to 8... const String abcd = F("ABCDEFGH"); // In case anyone dares to extend VARS_PER_TASK to 8...
const uint16_t endCheck = P129_CONFIG_CHIP_COUNT + 4; // 4(.0) = nr of bytes in an uint32_t. const uint16_t endCheck = P129_CONFIG_CHIP_COUNT + 4; // 4(.0) = nr of bytes in an uint32_t.
const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK), static_cast<uint8_t>(ceil(P129_CONFIG_CHIP_COUNT / 4.0f))); const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK), static_cast<uint8_t>(ceilf(P129_CONFIG_CHIP_COUNT / 4.0f)));
uint8_t dotInsert;
uint8_t dotOffset;
for (uint16_t varNr = 0; varNr < maxVar; ++varNr) { for (uint16_t varNr = 0; varNr < maxVar; ++varNr) {
if (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY) { if (P129_CONFIG_FLAGS_GET_VALUES_DISPLAY) {
label = F("Bin"); label = F("Bin");
state = F("0b"); state = F("0b");
dotInsert = 10;
dotOffset = 9;
} else { } else {
label = F("Hex"); label = F("Hex");
state = F("0x"); state = F("0x");
dotInsert = 4;
dotOffset = 3;
} }
label += strformat(F(" State_%s "), abcd.substring(varNr, varNr + 1).c_str()); label += strformat(F(" State_%s "), abcd.substring(varNr, varNr + 1).c_str());
@@ -435,11 +419,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string)
label += (P129_CONFIG_SHOW_OFFSET + (4 * varNr) + 1); // 4 = nr of bytes in an uint32_t. label += (P129_CONFIG_SHOW_OFFSET + (4 * varNr) + 1); // 4 = nr of bytes in an uint32_t.
if ((P129_CONFIG_SHOW_OFFSET + (4 * varNr) + 4) <= endCheck) { // Only show if still in range if ((P129_CONFIG_SHOW_OFFSET + (4 * varNr) + 4) <= endCheck) { // Only show if still in range
state += P129_ul2stringFixed(UserVar.getUint32(event->TaskIndex, varNr), P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : HEX); state += ul2stringFixed(UserVar.getUint32(event->TaskIndex, varNr), P129_CONFIG_FLAGS_GET_VALUES_DISPLAY ? BIN : HEX, true);
for (uint8_t i = 0; i < 3; ++i, dotInsert += dotOffset) { // Insert readability separators
state = state.substring(0, dotInsert) + '.' + state.substring(dotInsert);
}
pluginWebformShowValue(event->TaskIndex, VARS_PER_TASK + varNr, label, state, true); pluginWebformShowValue(event->TaskIndex, VARS_PER_TASK + varNr, label, state, true);
} }
} }
@@ -95,6 +95,42 @@ String ll2String(int64_t value, uint8_t base) {
} }
} }
/**
* format an uint32_t with 0-prefixed in the provided base of 2, 16 and insert an optional dot as separator for each byte
* separator only applied for base 2 and 16
*/
String ul2stringFixed(uint32_t value, uint8_t base, bool dotSeparator) {
// Set bit just left of 32 bits so we will see the leading zeroes
const uint64_t val = static_cast<uint64_t>(value) | 0x100000000ull;
String valStr = ull2String(val, base).substring(1); // Delete leading 1 we added
if (base == HEX) {
valStr.toUpperCase(); // uppercase hex for readability
}
if (dotSeparator) {
uint8_t dotInsert{};
uint8_t dotOffset{};
if (BIN == base) {
dotInsert = 10;
dotOffset = 9;
} else
if (HEX == base) {
dotInsert += 4;
dotOffset = 3;
}
if (dotInsert) {
for (uint8_t i = 0; i < 3; ++i, dotInsert += dotOffset) { // Insert readability separators
valStr = valStr.substring(0, dotInsert) + '.' + valStr.substring(dotInsert);
}
}
}
return valStr;
}
String trimTrailingZeros(const String& value) { String trimTrailingZeros(const String& value) {
String res(value); String res(value);
int dot_pos = res.lastIndexOf('.'); int dot_pos = res.lastIndexOf('.');
@@ -35,6 +35,11 @@ String ull2String(uint64_t value,
String ll2String(int64_t value, String ll2String(int64_t value,
uint8_t base = 10); uint8_t base = 10);
String ul2stringFixed(uint32_t value,
uint8_t base,
bool dotSeparator);
String trimTrailingZeros(const String& value); String trimTrailingZeros(const String& value);
String toStringNoZero(int64_t value); String toStringNoZero(int64_t value);
+1 -1
View File
@@ -35,7 +35,7 @@ bool P126_data_struct::plugin_init(struct EventStruct *event) {
} }
const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK), const uint16_t maxVar = min(static_cast<uint8_t>(VARS_PER_TASK),
static_cast<uint8_t>(ceil((P126_CONFIG_CHIP_COUNT - P126_CONFIG_SHOW_OFFSET) / 4.0))); static_cast<uint8_t>(ceilf((P126_CONFIG_CHIP_COUNT - P126_CONFIG_SHOW_OFFSET) / 4.0f)));
uint32_t par; uint32_t par;
for (uint16_t varNr = 0; varNr < maxVar; ++varNr) { for (uint16_t varNr = 0; varNr < maxVar; ++varNr) {
+23
View File
@@ -519,6 +519,29 @@ void handle_json()
#endif // if FEATURE_STRING_VARIABLES #endif // if FEATURE_STRING_VARIABLES
uom); uom);
} }
if (Device[DeviceIndex].HasFormatUserVar) {
struct EventStruct TempEvent(TaskIndex);
for (uint8_t x = 0; x < valueCount; x++)
{
String value;
TempEvent.idx = x;
TempEvent.ParN[x] = 1; // Get formatted version of the value
PluginCall(PLUGIN_FORMAT_USERVAR, &TempEvent, value);
if (!value.isEmpty()) {
handle_json_stream_task_value_data(taskValueWriter.get(),
VARS_PER_TASK + x + 1,
Cache.getTaskDeviceValueName(TaskIndex, x),
255,
value,
EMPTY_STRING,
EMPTY_STRING);
}
}
// FIXME tonhuisman: HasFormatUserVar is not really compatible with Derived Values...
}
#if FEATURE_STRING_VARIABLES #if FEATURE_STRING_VARIABLES
if (Settings.ShowDerivedTaskValues(TaskIndex)) { if (Settings.ShowDerivedTaskValues(TaskIndex)) {