[JSON] Merge formatting code DerivedTaskValues for web and JSON

This commit is contained in:
TD-er committed 2026-09-05 22:55:59 +02:00
1 parent e938d4c7a9
commit efc0525f31
8 files changed
+277 -280

No files matched your search

+17 -90
View File
@@ -7,16 +7,11 @@
#include "src/DataStructs/SettingsStruct.h"
#include "src/DataStructs/TimingStats.h"
#include "src/Globals/Cache.h"
# if FEATURE_STRING_VARIABLES
#include "src/Globals/Device.h"
# endif // #if FEATURE_STRING_VARIABLES
#include "src/Globals/Plugins.h"
#include "src/Globals/Settings.h"
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
#include "src/Helpers/ESPEasy_UnitOfMeasure.h"
#endif
#include "src/Helpers/Misc.h"
#include "src/Helpers/StringParser.h"
#include "src/Helpers/TaskValuesWriterHelper.h"
PluginTaskData_base *Plugin_task_data[TASKS_MAX] = {};
@@ -223,92 +218,24 @@ bool pluginTaskData_initialized(taskIndex_t taskIndex) {
(Plugin_task_data[taskIndex]->_taskdata_pluginID == Settings.getPluginID_for_task(taskIndex));
}
pluginWebformShowValue_struct::pluginWebformShowValue_struct(struct EventStruct *e)
: event(e) {
if (e) {
deviceIndex = getDeviceIndex_from_TaskIndex(event->TaskIndex);
valueCount = getValueCountForTask(event->TaskIndex);
}
}
void pluginWebformShowValue_struct::clear()
{
valName.clear();
valName_id.clear();
value.clear();
value_id.clear();
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
uom.clear();
#endif
# if FEATURE_STRING_VARIABLES
hasPresentation = false;
presentation.clear();
#endif
nrDecimals=0;
}
bool pluginWebformShowValue_struct::initRegularTaskValue(uint8_t varNr)
{
if (!validDeviceIndex(deviceIndex)) return false;
if (varNr >= valueCount) return false;
valueNumber = varNr;
// Make sure we can re-use the same struct for multiple task values
clear();
// Need to produce the 'regular' task values
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
if (Settings.ShowUnitOfMeasureOnDevicesPage()) {
const uint8_t uomIndex = Cache.getTaskVarUnitOfMeasure(event->TaskIndex, varNr);
if ((uomIndex != 0)) {
uom = toUnitOfMeasureName(uomIndex);
}
}
# endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
nrDecimals = Cache.getTaskDeviceValueDecimals(event->TaskIndex, varNr);
value = formatUserVarNoCheck(event, varNr);
# if FEATURE_STRING_VARIABLES
const DeviceStruct& device = Device[deviceIndex];
bool hasPresentation = false;
if (!device.HideDerivedValues) {
presentation = formatUserVarForPresentation(event, varNr, hasPresentation, value, deviceIndex);
}
# endif // if FEATURE_STRING_VARIABLES
valName = Cache.getTaskDeviceValueName(event->TaskIndex, varNr);
setID(varNr);
return true;
}
void pluginWebformShowValue_struct::setID(uint8_t varNr)
{
valueNumber = varNr;
const String id_postfix = strformat(F("%u_%u"), event->TaskIndex, varNr);
valName_id = concat(F("valuename_"), id_postfix);
value_id = concat(F("value_"), id_postfix);
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
if (!uom.isEmpty())
uom_id = concat(F("uom_"), id_postfix);
#endif
# if FEATURE_STRING_VARIABLES
if (!presentation.isEmpty())
presentation_id = concat(F("pres_"), id_postfix);
#endif
}
void pluginWebformShowValue(
struct EventStruct *event,
const String &label,
const String &value,
bool addTrailingBreak)
TaskValuesWriterHelper *data)
{
if (!data || !data->event) return;
if (data->valueNumber > 0) {
addHtmlDiv(F("div_br"));
}
String value;
value = data->value;
if (data->hasPresentation) value = data->presentation;
else if (!data->uom.isEmpty())
value += concat(' ', data->uom);
pluginWebformShowValue(
data->valName, data->valName_id,
data->value, data->value_id,
data->addTrailingBreak);
}
+5 -35
View File
@@ -10,7 +10,6 @@
#include "src/DataStructs/PinMode.h"
#include "src/DataStructs/PluginTaskData_base.h"
#include "src/DataTypes/DeviceIndex.h"
#include "src/DataTypes/ESPEasy_plugin_functions.h"
#include "src/ESPEasyCore/Controller.h"
@@ -162,6 +161,8 @@ int8_t & do_PIN(DEBUG_PCONFIG_RANGE_CHECK_args_decl);
# define CONFIG_PORT (Settings.TaskDevicePort[event->TaskIndex])
#endif // ifndef CONFIG_PORT
// Forward declaration to prevent cyclical dependencies
struct TaskValuesWriterHelper;
extern PluginTaskData_base *Plugin_task_data[TASKS_MAX];
@@ -193,42 +194,11 @@ String getPluginCustomArgName(const __FlashStringHelper *label,
int getFormItemIntCustomArgName(int varNr);
// Helper function to create formatted custom values for display in the devices overview page.
// When called from PLUGIN_WEBFORM_SHOW_VALUES, the last item should add a traling div_br class
// if the regular values should also be displayed.
// The call to PLUGIN_WEBFORM_SHOW_VALUES should only return success = true when no regular values should be displayed
// Note that the varNr of the custom values should not conflict with the existing variable numbers (e.g. start at VARS_PER_TASK)
void pluginWebformShowValue(
struct EventStruct *event,
bool addTrailingBreak = false);
TaskValuesWriterHelper *data);
struct pluginWebformShowValue_struct {
pluginWebformShowValue_struct(struct EventStruct *event);
void clear();
bool initRegularTaskValue(uint8_t varNr);
void setID(uint8_t varNr);
EventStruct * const event = nullptr;
String valName, valName_id, value, value_id;
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
String uom, uom_id;
#endif
# if FEATURE_STRING_VARIABLES
String presentation, presentation_id;
bool hasPresentation = false;
#endif
uint8_t nrDecimals{};
uint8_t valueCount{};
uint8_t valueNumber{};
deviceIndex_t deviceIndex = INVALID_DEVICE_INDEX;
bool addTrailingBreak = false;
};
void pluginWebformShowValue(
struct EventStruct *event,
+2
View File
@@ -156,5 +156,7 @@ protected:
bool _allowFormatOverrides = true;
bool _tabularFormat = true;
}; // class KeyValueWriter
+192
View File
@@ -0,0 +1,192 @@
#include "../Helpers/TaskValuesWriterHelper.h"
#include "../../ESPEasy_common.h"
#include "../../_Plugin_Helper.h"
#if FEATURE_STRING_VARIABLES
# include "../Globals/Device.h"
#endif // #if FEATURE_STRING_VARIABLES
#include "../Globals/Plugins.h"
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
# include "../Helpers/ESPEasy_UnitOfMeasure.h"
#endif
#include "../WebServer/JSON.h"
TaskValuesWriterHelper::TaskValuesWriterHelper(struct EventStruct *e)
: event(e) {
if (e) {
deviceIndex = getDeviceIndex_from_TaskIndex(event->TaskIndex);
valueCount = getValueCountForTask(event->TaskIndex);
}
}
void TaskValuesWriterHelper::clear()
{
valName.clear();
valName_id.clear();
value.clear();
value_id.clear();
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
uom.clear();
#endif
#if FEATURE_STRING_VARIABLES
hasPresentation = false;
presentation.clear();
#endif // if FEATURE_STRING_VARIABLES
nrDecimals = 0;
}
void TaskValuesWriterHelper::writeRegularTaskValues()
{
for (uint8_t x = 0; x < valueCount; x++)
{
if (initRegularTaskValue(x)) {
write();
}
}
}
#if FEATURE_STRING_VARIABLES
void TaskValuesWriterHelper::writeDerivedTaskValues()
{
if (!validDeviceIndex(deviceIndex)) { return; }
const DeviceStruct& device = Device[deviceIndex];
int varNr = VARS_PER_TASK;
if (Settings.ShowDerivedTaskValues(event->TaskIndex)) {
String taskName = getTaskDeviceName(event->TaskIndex);
taskName.toLowerCase();
String postfix;
const String search = getDerivedValueSearchAndPostfix(taskName, postfix);
auto it = customStringVar.begin();
while (it != customStringVar.end()) {
if (it->first.startsWith(search) && it->first.endsWith(postfix)) {
clear();
valName = it->first.substring(search.length(), it->first.indexOf('-'));
String vType;
const String vname2 = getDerivedValueNameUomAndVType(taskName, valName, uom, vType);
if (!vname2.isEmpty()) {
valName = vname2;
}
if (!it->second.isEmpty()) {
value = it->second;
// FIXME TD-er: Why these differences between JSON and Web?
if (event->kvWriter)
{
stripEscapeCharacters(value);
value = parseTemplate(value);
nrDecimals = 255; // FIXME Use the minimal number of decimals needed
presentation =
formatUserVarForPresentation(
event,
INVALID_TASKVAR_INDEX,
hasPresentation,
value,
deviceIndex,
valName);
} else {
value = parseTemplateAndCalculate(value);
presentation = getCustomStringVar(strformat(
F(TASK_VALUE_PRESENTATION_PREFIX_TEMPLATE),
taskName.c_str(),
valName.c_str()));
if (!presentation.isEmpty()) {
stripEscapeCharacters(presentation);
presentation.replace(F("%value%"), value);
presentation = parseTemplate(presentation);
}
}
setID(varNr);
write();
++varNr;
}
}
// FIXME TD-er: Search for "compareTo(search)" in the code to find lots of nearly duplicate code
else if (it->first.substring(0, search.length()).compareTo(search) > 0) {
break;
}
++it;
}
}
}
#endif // if FEATURE_STRING_VARIABLES
bool TaskValuesWriterHelper::initRegularTaskValue(uint8_t varNr)
{
if (!validDeviceIndex(deviceIndex)) { return false; }
if (varNr >= valueCount) { return false; }
valueNumber = varNr;
// Make sure we can re-use the same struct for multiple task values
clear();
// Need to produce the 'regular' task values
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
if (Settings.ShowUnitOfMeasureOnDevicesPage()) {
const uint8_t uomIndex = Cache.getTaskVarUnitOfMeasure(event->TaskIndex, varNr);
if ((uomIndex != 0)) {
uom = toUnitOfMeasureName(uomIndex);
}
}
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
nrDecimals = Cache.getTaskDeviceValueDecimals(event->TaskIndex, varNr);
value = formatUserVarNoCheck(event, varNr);
#if FEATURE_STRING_VARIABLES
const DeviceStruct& device = Device[deviceIndex];
bool hasPresentation = false;
if (!device.HideDerivedValues) {
presentation = formatUserVarForPresentation(event, varNr, hasPresentation, value, deviceIndex);
}
#endif // if FEATURE_STRING_VARIABLES
valName = Cache.getTaskDeviceValueName(event->TaskIndex, varNr);
setID(varNr);
return true;
}
void TaskValuesWriterHelper::setID(uint8_t varNr)
{
if (!validDeviceIndex(deviceIndex)) { return; }
valueNumber = varNr;
const String id_postfix = strformat(F("%u_%u"), event->TaskIndex, varNr);
valName_id = concat(F("valuename_"), id_postfix);
value_id = concat(F("value_"), id_postfix);
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
if (!uom.isEmpty()) {
uom_id = concat(F("uom_"), id_postfix);
}
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
#if FEATURE_STRING_VARIABLES
if (hasPresentation) {
presentation_id = concat(F("pres_"), id_postfix);
}
#endif // if FEATURE_STRING_VARIABLES
}
void TaskValuesWriterHelper::write()
{
if (!validDeviceIndex(deviceIndex)) { return; }
if (event->kvWriter) { handle_json_stream_task_value_data(this); }
else { pluginWebformShowValue(this); }
}
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#include "../../ESPEasy_common.h"
#include "../DataTypes/DeviceIndex.h"
// Helper function to create formatted custom values for display in the devices overview page.
// When called from PLUGIN_WEBFORM_SHOW_VALUES, the last item should add a traling div_br class
// if the regular values should also be displayed.
// The call to PLUGIN_WEBFORM_SHOW_VALUES should only return success = true when no regular values should be displayed
// Note that the varNr of the custom values should not conflict with the existing variable numbers (e.g. start at VARS_PER_TASK)
struct TaskValuesWriterHelper {
TaskValuesWriterHelper(struct EventStruct *event);
void clear();
void writeRegularTaskValues();
#if FEATURE_STRING_VARIABLES
void writeDerivedTaskValues();
#endif
bool initRegularTaskValue(uint8_t varNr);
void setID(uint8_t varNr);
void write();
EventStruct * const event = nullptr;
String valName, valName_id, value, value_id;
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
String uom, uom_id;
#endif
#if FEATURE_STRING_VARIABLES
String presentation, presentation_id;
bool hasPresentation = false;
#endif // if FEATURE_STRING_VARIABLES
uint8_t nrDecimals{};
uint8_t valueCount{};
uint8_t valueNumber{};
deviceIndex_t deviceIndex = INVALID_DEVICE_INDEX;
bool addTrailingBreak = false;
};
+6 -92
View File
@@ -34,6 +34,7 @@
# include "../Helpers/SPI_Helper.h"
# include "../Helpers/StringConverter.h"
# include "../Helpers/StringGenerator_GPIO.h"
# include "../Helpers/TaskValuesWriterHelper.h"
# include "../../_Plugin_Helper.h"
@@ -859,99 +860,12 @@ void handle_devicess_ShowAllTasksTable(uint8_t page)
{
if (validPluginID_fullcheck(pid))
{
const uint8_t valueCount = getValueCountForTask(x);
for (uint8_t varNr = 0; varNr < valueCount; varNr++)
{
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
const uint8_t uomIndex = Cache.getTaskVarUnitOfMeasure(x, varNr);
String uom;
if ((uomIndex != 0) && Settings.ShowUnitOfMeasureOnDevicesPage()) {
uom = concat(' ', toUnitOfMeasureName(uomIndex));
}
# endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
const String value = formatUserVarNoCheck(&TempEvent, varNr);
# if FEATURE_STRING_VARIABLES
bool hasPresentation = false;
String presentation;
if (!device.HideDerivedValues) {
presentation = formatUserVarForPresentation(&TempEvent, varNr, hasPresentation, value, DeviceIndex);
}
# endif // if FEATURE_STRING_VARIABLES
pluginWebformShowValue(
x,
varNr,
Cache.getTaskDeviceValueName(x, varNr),
# if FEATURE_STRING_VARIABLES
hasPresentation ? presentation :
# endif // if FEATURE_STRING_VARIABLES
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
concat(value, uom)
# else // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
value
# endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
);
}
TaskValuesWriterHelper data(&TempEvent);
data.writeRegularTaskValues();
# if FEATURE_STRING_VARIABLES
data.writeDerivedTaskValues();
#endif
}
# if FEATURE_STRING_VARIABLES
if (!device.HideDerivedValues) {
int varNr = VARS_PER_TASK;
if (Settings.ShowDerivedTaskValues(x)) {
String taskName = getTaskDeviceName(x);
taskName.toLowerCase();
String postfix;
const String search = getDerivedValueSearchAndPostfix(taskName, postfix);
auto it = customStringVar.begin();
while (it != customStringVar.end()) {
if (it->first.startsWith(search) && it->first.endsWith(postfix)) {
String valueName = it->first.substring(search.length(), it->first.indexOf('-'));
String uom;
String vType;
const String vname2 = getDerivedValueNameUomAndVType(taskName, valueName, uom, vType);
if (!vname2.isEmpty()) {
valueName = vname2;
}
if (!it->second.isEmpty()) {
String value(it->second);
value = parseTemplateAndCalculate(value);
String presentation = getCustomStringVar(strformat(F(TASK_VALUE_PRESENTATION_PREFIX_TEMPLATE),
taskName.c_str(),
valueName.c_str()));
if (!uom.isEmpty()) {
value = strformat(F("%s %s"), value.c_str(), uom.c_str());
}
if (!presentation.isEmpty()) {
stripEscapeCharacters(presentation);
presentation.replace(F("%value%"), value);
value = parseTemplate(presentation);
}
pluginWebformShowValue(
x,
varNr,
valueName,
value);
++varNr;
}
}
else if (it->first.substring(0, search.length()).compareTo(search) > 0) {
break;
}
++it;
}
}
}
# endif // if FEATURE_STRING_VARIABLES
}
}
}
+8 -61
View File
@@ -3,6 +3,7 @@
#include "../WebServer/JSON.h"
#include "../WebServer/Markup_Forms.h"
#include "../DataStructs/KeyValueStruct.h"
#include "../DataStructs/TimingStats.h"
#include "../Globals/Cache.h"
@@ -19,6 +20,7 @@
#include "../Helpers/StringConverter.h"
#include "../Helpers/StringProvider.h"
#include "../Helpers/StringGenerator_System.h"
#include "../Helpers/TaskValuesWriterHelper.h"
#include "../../ESPEasy/net/Helpers/NW_info_writer.h"
@@ -486,67 +488,12 @@ void handle_json()
struct EventStruct TempEvent(TaskIndex);
TempEvent.kvWriter = taskValueWriter.get();
TaskValuesWriterHelper data(&TempEvent);
// FIXME tonhuisman: HasFormatUserVar is not really compatible with Derived Values...
for (uint8_t x = 0; x < valueCount; x++)
{
pluginWebformShowValue_struct data(&TempEvent);
data.initRegularTaskValue(x);
handle_json_stream_task_value_data(&data);
}
#if FEATURE_STRING_VARIABLES
if (Settings.ShowDerivedTaskValues(TaskIndex)) {
int varNr = VARS_PER_TASK;
String taskName = getTaskDeviceName(TaskIndex);
taskName.toLowerCase();
String postfix;
const String search = getDerivedValueSearchAndPostfix(taskName, postfix);
auto it = customStringVar.begin();
while (it != customStringVar.end()) {
if (it->first.startsWith(search) && it->first.endsWith(postfix)) {
String valueName = it->first.substring(search.length(), it->first.indexOf('-'));
String uom;
String vType;
const String vname2 = getDerivedValueNameUomAndVType(taskName, valueName, uom, vType);
if (!vname2.isEmpty()) {
valueName = vname2;
}
if (!it->second.isEmpty()) {
String value(it->second);
stripEscapeCharacters(value);
value = parseTemplate(value);
uint8_t nrDecimals = 255; // FIXME Use the minimal number of decimals needed
bool hasPresentation;
const String presentation =
formatUserVarForPresentation(&TempEvent,
INVALID_TASKVAR_INDEX,
hasPresentation,
value,
DeviceIndex,
valueName);
handle_json_stream_task_value_data(taskValueWriter.get(),
varNr + 1,
valueName,
nrDecimals,
value,
presentation,
uom);
++varNr;
}
}
else if (it->first.substring(0, search.length()).compareTo(search) > 0) {
break;
}
++it;
}
}
#endif // if FEATURE_STRING_VARIABLES
data.writeRegularTaskValues();
# if FEATURE_STRING_VARIABLES
data.writeDerivedTaskValues();
#endif
}
}
@@ -656,7 +603,7 @@ void handle_json()
STOP_TIMER(HANDLE_SERVING_WEBPAGE_JSON);
}
void handle_json_stream_task_value_data(pluginWebformShowValue_struct* data)
void handle_json_stream_task_value_data(TaskValuesWriterHelper* data)
{
if (!data || !data->event || !data->event->kvWriter) return;
+2 -2
View File
@@ -5,7 +5,7 @@
#include "../WebServer/common.h"
class KeyValueWriter;
struct pluginWebformShowValue_struct;
struct TaskValuesWriterHelper;
// ********************************************************************************
// Web Interface get CSV value from task
@@ -20,7 +20,7 @@ void handle_csvval();
// ********************************************************************************
void handle_json();
void handle_json_stream_task_value_data(pluginWebformShowValue_struct* data);
void handle_json_stream_task_value_data(TaskValuesWriterHelper* data);
void handle_json_stream_task_value_data(KeyValueWriter* parent,
uint16_t valueNumber,