[UoM] Refactoring to reduce page-load time and size

This commit is contained in:
Ton Huisman
2025-10-25 16:06:31 +02:00
parent d4c1ac6628
commit 0740207f45
21 changed files with 461 additions and 172 deletions
+3 -2
View File
@@ -250,10 +250,10 @@ If set the UoM will be space-appended to the value when displayed on the Devices
A list of 165+ Unit of Measure values is available (derived of what's supported/expected by Home Assistant):
``°C, °F, K, %, Pa, hPa, bar, mbar, inHg, psi, W, kW, V, Wh, kWh, A, VA, mm, cm, m, km,`` ``L, mL, m³, ft³, m³/h, ft³/h, lx, UV index, µg/m³, mg/m³, p/m³, ppm, ppb,``
``°, €, $, ¢, µs, ms, s, min, h, d, w, m, y, in, ft, yd, mi, Hz, GHz, gal, fl. oz, m²,`` ``g, kg, mg, µg, oz, lb, µS/cm, W/m², mm/h, mm/s, in/s, m/s, in/h, km/h, mph, db, dBm,``
``°, €, $, ¢, µs, ms, s, min, h, d, w, m, y, in, ft, yd, mi, Hz, GHz, gal, fl. oz, m²,`` ``g, kg, mg, µg, oz, lb, µS/cm, W/m², mm/h, mm/s, in/s, m/s, in/h, km/h, mph, dB, dBm,``
``bit, kbit, Mbit, Gbit, B, kB, MB, GB, TB, PB, EB, ZB, YB, KiB, MiB, GiB, TiB, PiB, EiB, ZiB, YiB,`` ``bit/s, kbit/s, Mbit/s, Gbit/s, B/s, kB/s, MB/s, GB/s, KiB/s, MiB/s, GiB/s,``
``ft/s, kn, mW, MW, GW, TW, BTU/(h·ft²), pH, cbar, mmHg, kPa, mA, µA, mV, µV, kV, cm², km², mm²,`` ``in², ft², yd², mi², ac, ha, kHz, MHz, mWh, MWh, GWh, TWh, cal, kcal, Mcal,``
``Gcal, J, kJ, MJ, GJ, var, kvar, varh, kvarh, st, mg/dL, mmol/L, µSv, µSv/h, m³/s, ft³/min,`` ``L/h, L/min, L/s, gal/min, mL/s, g/m³, kWh/100km, Wh/km, mi/kWh, km/kWh, in/d, mm/d``
``Gcal, J, kJ, MJ, GJ, var, kvar, varh, kvarh, st, mg/dL, mmol/L, µSv, µSv/h, m³/s, ft³/min,`` ``L/h, L/min, L/s, gal/min, mL/s, g/m³, kWh/100km, Wh/km, mi/kWh, km/kWh, in/d, mm/d, Ah``
Displaying the Unit of Measure in the Devices overview page can be disabled by unchecking the **Show Unit of Measure** checkbox on the Tools/Advanced page.
@@ -261,6 +261,7 @@ The **Unit of Measure** selector is, because of the vast number of options, divi
.. image:: Task_config_page_UoM_selector_part.png
Depending on the available data, the number of options is limited as much as possible, to reduce page-load time, as filling this many option in multiple comboboxes takes quite some time.
Value Type
^^^^^^^^^^
+1
View File
@@ -8,6 +8,7 @@
# include "src/Globals/Plugins.h"
# include "src/Globals/Statistics.h"
# include "src/Helpers/_CPlugin_Helper_mqtt.h"
# include "src/Helpers/ESPEasy_UnitOfMeasure.h"
# include "src/Helpers/PeriodicalActions.h"
# include "_Plugin_Helper.h"
+15
View File
@@ -13,6 +13,7 @@
*/
/** Changelog:
* 2025-10-25 tonhuisman: Add custom Unit of Measure group per value to limit selection to useful options
* 2025-08-02 repa6: Add partial support for TAC2100 meter
* 2025-01-17 tonhuisman: Implement support for MQTT AutoDiscovery (partial)
* 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported yet for Eastron)
@@ -103,6 +104,20 @@ boolean Plugin_078(uint8_t function, struct EventStruct *event, String& string)
}
# endif // if FEATURE_MQTT_DISCOVER
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
case PLUGIN_GET_UOM_GROUPS:
{
const SDM_MODEL model = static_cast<SDM_MODEL>(P078_MODEL);
for (uint8_t i = 0; i < P078_NR_OUTPUT_VALUES; ++i) {
const uint8_t choice = PCONFIG(i + P078_QUERY1_CONFIG_POS);
event->Par64N[i] = Plugin_078_QueryUOMGroup(model, choice);
}
success = true;
break;
}
# endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
case PLUGIN_WEBFORM_SHOW_CONFIG:
{
string += serialHelper_getSerialTypeLabel(event);
+1
View File
@@ -16,6 +16,7 @@
#include "../Globals/RuntimeData.h"
#include "../Globals/Settings.h"
#include "../Helpers/ESPEasy_UnitOfMeasure.h"
#include "../Helpers/Misc.h"
#include "../Helpers/Numerical.h"
#include "../Helpers/StringConverter.h"
+26 -1
View File
@@ -67,8 +67,33 @@ public:
int Par3;
int Par4;
int Par5;
int Par6;
int Par7;
int Par8;
};
int ParN[5] = { 0 };
struct {
float Parf_1;
float Parf_2;
float Parf_3;
float Parf_4;
float Parf_5;
float Parf_6;
float Parf_7;
float Parf_8;
};
float ParfN[8];
struct {
int64_t Par64_1;
int64_t Par64_2;
int64_t Par64_3;
int64_t Par64_4;
};
int64_t Par64N[4];
int ParN[8] = { 0 };
};
// The origin of the values in the event. See EventValueSource.h
+1 -1
View File
@@ -11,7 +11,7 @@
# include "../WebServer/Chart_JS.h"
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
# include "../WebServer/Markup.h"
# include "../Helpers/ESPEasy_UnitOfMeasure.h"
# endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
PluginStats_array::~PluginStats_array()
@@ -69,6 +69,9 @@ enum PluginFunctions_e {
#if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
PLUGIN_GET_DISCOVERY_VTYPES , // Fetch the Sensor_VType values for dynamic value configurations in event->Par1..Par4
#endif // IF FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
PLUGIN_GET_UOM_GROUPS , // Fetch the UoM groups in a bitmap per value, as found in ESPEasy_UnitOfMeasure.cpp unit_of_measure_labels with -1024 offset
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
PLUGIN_MAX_FUNCTION // Leave as last one.
};
+10 -4
View File
@@ -1006,9 +1006,12 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str)
#ifdef USES_ESPEASY_NOW
case PLUGIN_FILTEROUT_CONTROLLER_DATA:
#endif // ifdef USES_ESPEASY_NOW
#if FEATURE_MQTT_DISCOVER
#if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
case PLUGIN_GET_DISCOVERY_VTYPES:
#endif // if FEATURE_MQTT_DISCOVER
#endif // if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
case PLUGIN_GET_UOM_GROUPS:
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
// PLUGIN_MQTT_xxx functions are directly called from the scheduler.
// case PLUGIN_MQTT_CONNECTION_STATE:
@@ -1026,9 +1029,12 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str)
(Function == PLUGIN_INIT_VALUE_RANGES) ||
(Function == PLUGIN_WEBFORM_SHOW_SERIAL_PARAMS) ||
(Function == PLUGIN_WEBFORM_PRE_SERIAL_PARAMS)
#if FEATURE_MQTT_DISCOVER
#if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
|| (Function == PLUGIN_GET_DISCOVERY_VTYPES)
#endif // if FEATURE_MQTT_DISCOVER
#endif // if FEATURE_MQTT_DISCOVER || FEATURE_CUSTOM_TASKVAR_VTYPE
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
|| (Function == PLUGIN_GET_UOM_GROUPS)
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
) {
LoadTaskSettings(event->TaskIndex);
}
+198
View File
@@ -0,0 +1,198 @@
#include "../Helpers/ESPEasy_UnitOfMeasure.h"
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
# include "../DataTypes/SensorVType.h"
// *** DO NOT CHANGE ORDER, SAVED IN TASK SETTINGS! ***
const char unit_of_measure_list[] PROGMEM =
"|" // 0 = Empty/none
"°C|°F|K|" // 1..3
"%|" // 4
"Pa|hPa|bar|mbar|inHg|psi|" // 5..10
"W|kW|" // 11..12
"V|" // 13
"Wh|kWh|" // 14..15
"A|VA|" // 16..17
"mm|cm|m|km|" // 18..21
"L|mL|m³|ft³|" // 22..25
"m³/h|ft³/h|" // 26..27
"lx|" // 28
"UV index|" // 29
"µg/m³|mg/m³|p/m³|ppm|ppb|" // 30..34
"°|" // 35
"€|$|¢|" // 36..38
"μs|ms|s|min|h|d|w|m|y|" // 39..47
"in|ft|yd|mi|" // 48..51
"Hz|GHz|" // 52..53
"gal|fl. oz|" // 54..55
"m²|" // 56
"g|kg|mg|µg|" // 57..60
"oz|lb|" // 61..62
"µS/cm|" // 63
"W/m²|" // 64
"mm/h|" // 65
"mm/s|in/s|m/s|in/h|km/h|mph|" // 66..71
"dB|dBm|" // 72..73
"bit|kbit|Mbit|Gbit|B|kB|MB|GB|TB|PB|EB|ZB|YB|KiB|MiB|GiB|TiB|PiB|EiB|ZiB|YiB|" // 74..94
"bit/s|kbit/s|Mbit/s|Gbit/s|B/s|kB/s|MB/s|GB/s|KiB/s|MiB/s|GiB/s|" // 95..105
"ft/s|kn|" // 106..107
"mW|MW|GW|TW|" // 108..111
"BTU/(h⋅ft²)|" // 112
"pH|" // 113
"cbar|mmHg|kPa|" // 114..116
"mA|µA|mV|µV|kV|" // 117..121
"cm²|km²|mm²|in²|ft²|yd²|mi²|ac|ha|" // 122..130
"kHz|MHz|" // 131..132
"mWh|MWh|GWh|TWh|cal|kcal|Mcal|Gcal|J|kJ|MJ|GJ|" // 133..144
"var|kvar|varh|kvarh|" // 145..148
"st|" // 149
"mg/dL|mmol/L|" // 150..151
"μSv|μSv/h|" // 152..153
"m³/s|ft³/min|L/h|L/min|L/s|gal/min|mL/s|" // 154..160
"g/m³|kWh/100km|Wh/km|mi/kWh|km/kWh|" // 161..165
"in/d|mm/d|" // 166 .. 167
"Ah|" // 168
; // *** DO NOT CHANGE ORDER, SAVED IN TASK SETTINGS! ***
// Not stored, when UoM index >= 1024 it's a label-index with 1024 subtracted
const char unit_of_measure_labels[] PROGMEM =
"Apparent power|Air quality/CO/CO2|Area|(Atmospheric) Pressure|" // A 1024..1027
"Blood glucose concentr.|" // B 1028
"Data rate|Data size|Distance|Duration|" // D 1029..1032
"Energy distance|Energy(-storage)|" // E 1033..1034
"Frequency|" // F 1035
"Gas|" // G 1036
"Percent Hum./Batt./Moist.|" // H 1037
"Illuminance|Irradiance|" // I 1038..1039
"Monetary|" // M 1040
"Nitrogen (di-/mon-)oxide|" // N 1041
"Voc/Ozone|" // O 1042
"Ph|PM/CO/CO2/NO(x)/Voc/Ozone|Power|" // P 1043..1045
"Radiation|Reactive energy/power|" // R 1046..1047
"Signal strength|Sound pressure|Speed|" // S 1048..1050
"Temperature|" // T 1051
"Voltage/Current|Volume/Water cons.|Volume flow rate|" // V 1052..1054
"Weight|Wind direction/angle|" // W 1055..1056
"Various units|" // Additional 1057
;
const uint16_t unit_of_measure_map[] PROGMEM = {
1051, 1, 2, 3, // Temperature
1037, 4, // Percent Battery,
// Humidity,
// Moisture
1027, 8, 6, 116, 7, 115, 10, 5, 114, 9, // (Atmospheric)
// Pressure
1052, 13, 119, 120, 121, 16, 117, 118, // Voltage/Current
1045, 11, 12, 108, 109, 110, 111, // Power
1024, 17, // Apparent power
1047, 145, 146, 147, 148, 168, // Reactive
// power/energy
1044, 30, 31, 32, 33, 34, // Particle matter
1031, 18, 19, 20, 21, 48, 49, 50, 51, // Distance
1055, 57, 58, 59, 60, 61, 62, 149, // Weight
1053, 22, 23, 24, 25, 54, 55, // Volume/Water
1054, 26, 27, 153, 154, 155, 156, 157, 158, 159, 160, // Volume flow rate
1032, 39, 40, 41, 42, 43, 44, 45, 46, 47, // Duration
1034, 14, 15, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, // Energy(-storage)
1033, 162, 163, 164, 165, // Energy distance
1050, 66, 67, 68, 69, 70, 71, 65, 106, 107, // Speed
1056, 35, // (Wind)
// direction/angle
1038, 28, // Illuminance
1039, 64, 112, // Irradiance
1046, 152, 153, // Radiation
1057, 29, 63, 161, 166, 167, // Various units
1035, 52, 53, 131, 132, // Frequency
1043, 113, // Potential
// hydrogen
1026, 56, 122, 123, 124, 125, 126, 127, 128, 129, 130, // Area
1029, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, // Data rate
1030, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,88, 89, 90, 91, 92, 93, 94, // Data size
1049, 72, 73, // Sound pressure
1028, 150, 151, // Blood glucose
1040, 36, 37, 38, // Monetary
};
const uint16_t unit_of_measure_map_size = NR_ELEMENTS(unit_of_measure_map);
String toUnitOfMeasureName(const uint32_t unitOfMeasureIndex,
const String & defUoM) {
char tmp[26]{}; // "PM/CO/CO2/NO(x)/Voc/Ozone" + \0
String result;
if (unitOfMeasureIndex < 1024) {
result = GetTextIndexed(tmp, sizeof(tmp), unitOfMeasureIndex, unit_of_measure_list);
} else {
result = GetTextIndexed(tmp, sizeof(tmp), unitOfMeasureIndex - 1024, unit_of_measure_labels);
}
return result.isEmpty() ? defUoM : result;
}
int getUnitOfMeasureIndex(const String& uomName) {
return GetCommandCode(uomName.c_str(), unit_of_measure_list);
}
/**
* getUoMGroupForUoM: Get the selector group for the requested Unit of Measurement name
*/
uint16_t getUoMGroupForUoM(const String& uom) {
return getUoMGroupForUoM(getUnitOfMeasureIndex(uom));
}
/**
* getUoMGroupForUoM: Get the selector group for the requested Unit of Measurement index
*/
uint16_t getUoMGroupForUoM(const int findUom) {
if ((findUom < 0)) { return 0; }
const uint16_t find = findUom;
uint16_t result = 0;
uint16_t last = 0;
for (uint16_t idx = 0; idx < unit_of_measure_map_size; ++idx) {
const uint16_t uomIdx = pgm_read_word_near(&unit_of_measure_map[idx]);
if (uomIdx < 1024) {
if (find == uomIdx) {
result = last;
break;
}
} else {
last = uomIdx;
}
}
return result;
}
/**
* getDefaultUoMforSensorVType: get the default UoM parameters into Par64[] array for Sensor_VType::SENSOR_TYPE_TEMP_HUM,
* Sensor_VType::SENSOR_TYPE_TEMP_BARO, Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO and Sensor_VType::SENSOR_TYPE_TEMP_HUM_BARO
*/
bool getDefaultUoMforSensorVType(EventStruct *event) {
switch (event->sensorType) { // These are the only default UoM values to be handled
case Sensor_VType::SENSOR_TYPE_TEMP_HUM:
event->Par64_1 = UOM_GROUP_TEMPERATURE;
event->Par64_2 = UOM_GROUP_PERC_HUM_BAT_MOIST;
return true;
case Sensor_VType::SENSOR_TYPE_TEMP_BARO:
event->Par64_1 = UOM_GROUP_TEMPERATURE;
event->Par64_2 = UOM_GROUP_PRESSURE;
return true;
case Sensor_VType::SENSOR_TYPE_TEMP_EMPTY_BARO:
event->Par64_1 = UOM_GROUP_TEMPERATURE;
event->Par64_2 = UOM_GROUP_NONE;
event->Par64_3 = UOM_GROUP_PRESSURE;
return true;
case Sensor_VType::SENSOR_TYPE_TEMP_HUM_BARO:
event->Par64_1 = UOM_GROUP_TEMPERATURE;
event->Par64_2 = UOM_GROUP_PERC_HUM_BAT_MOIST;
event->Par64_3 = UOM_GROUP_PRESSURE;
return true;
default:
break;
}
return false;
}
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
+68
View File
@@ -0,0 +1,68 @@
#pragma once
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
# include "../DataStructs/ESPEasy_EventStruct.h"
# include "../Helpers/StringConverter.h"
const uint64_t UOM_GROUP_APPARENT_POWER = 0x0000000000000001; // 1024
const uint64_t UOM_GROUP_AIR_QUALITY = 0x0000000000000002;
const uint64_t UOM_GROUP_AREA = 0x0000000000000004;
const uint64_t UOM_GROUP_PRESSURE = 0x0000000000000008;
const uint64_t UOM_GROUP_BLOOD_GLUCOSE = 0x0000000000000010;
const uint64_t UOM_GROUP_DATA_RATE = 0x0000000000000020;
const uint64_t UOM_GROUP_DATA_SIZE = 0x0000000000000040; // 1030
const uint64_t UOM_GROUP_DISTANCE = 0x0000000000000080;
const uint64_t UOM_GROUP_DURATION = 0x0000000000000100;
const uint64_t UOM_GROUP_ENERGY_DISTANCE = 0x0000000000000200;
const uint64_t UOM_GROUP_ENERGY_STORAGE = 0x0000000000000400;
const uint64_t UOM_GROUP_FREQUENCY = 0x0000000000000800; // 1035
const uint64_t UOM_GROUP_GAS = 0x0000000000001000;
const uint64_t UOM_GROUP_PERC_HUM_BAT_MOIST = 0x0000000000002000;
const uint64_t UOM_GROUP_ILLUMINANCE = 0x0000000000004000;
const uint64_t UOM_GROUP_IRRADIANCE = 0x0000000000008000;
const uint64_t UOM_GROUP_MONETARY = 0x0000000000010000; // 1040
const uint64_t UOM_GROUP_NITROGEN_OXIDE = 0x0000000000020000;
const uint64_t UOM_GROUP_VOC_OZONE = 0x0000000000040000;
const uint64_t UOM_GROUP_PH = 0x0000000000080000;
const uint64_t UOM_GROUP_PM_CO_CO2_NOX = 0x0000000000100000;
const uint64_t UOM_GROUP_POWER = 0x0000000000200000; // 1045
const uint64_t UOM_GROUP_RADIATION = 0x0000000000400000;
const uint64_t UOM_GROUP_REACTIVE_POWER = 0x0000000000800000;
const uint64_t UOM_GROUP_SIGNAL_STRENGTH = 0x0000000001000000;
const uint64_t UOM_GROUP_SOUND_PRESSURE = 0x0000000002000000;
const uint64_t UOM_GROUP_SPEED = 0x0000000004000000; // 1050
const uint64_t UOM_GROUP_TEMPERATURE = 0x0000000008000000;
const uint64_t UOM_GROUP_VOLTAGE_CURRENT = 0x0000000010000000;
const uint64_t UOM_GROUP_VOLUME_WATER_CONS = 0x0000000020000000;
const uint64_t UOM_GROUP_VOLUME_FLOW_RATE = 0x0000000040000000;
const uint64_t UOM_GROUP_WEIGHT = 0x0000000080000000; // 1055
const uint64_t UOM_GROUP_WIND_DIRECTION = 0x0000000100000000;
const uint64_t UOM_GROUP_VARIOUS_UNITS = 0x0000000200000000;
const uint64_t UOM_GROUP_ALL = std::numeric_limits<uint64_t>::max();
const uint64_t UOM_GROUP_NONE = 0x0;
# define UOM_dB 72
# define UOM_dBm 73
# define UOM_milliVolt 119
# define UOM_Mbps 97 // Mbit/s
# define UOM_usec 39
# define UOM_GHz 53
# define UOM_MHz 132
# define UOM_ppm 33
# define UOM_degC 1
# define UOM_kB 79
# define UOM_Byte 78
# define UOM_percent 4
extern const uint16_t unit_of_measure_map[] PROGMEM;
extern const uint16_t unit_of_measure_map_size;
String toUnitOfMeasureName(const uint32_t unitOfMeasureIndex,
const String & defUoM = EMPTY_STRING);
int getUnitOfMeasureIndex(const String& uomName);
bool getDefaultUoMforSensorVType(EventStruct *event);
uint16_t getUoMGroupForUoM(const String& uom);
uint16_t getUoMGroupForUoM(const int findUom);
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
+2 -2
View File
@@ -104,9 +104,9 @@ void run_compiletime_checks() {
check_max_size<ExtraTaskSettingsStruct, DAT_TASKS_SIZE>();
#if ESP_IDF_VERSION_MAJOR > 3
// String class has increased with 4 bytes
check_size<EventStruct, 124u>(); // Is not stored
check_size<EventStruct, 136u>(); // Is not stored
#else
check_size<EventStruct, 104u>(); // Is not stored
check_size<EventStruct, 120u>(); // Is not stored
#endif
+1
View File
@@ -15,6 +15,7 @@
#include "../Helpers/ESPEasy_FactoryDefault.h"
#include "../Helpers/ESPEasy_Storage.h"
#include "../Helpers/ESPEasy_UnitOfMeasure.h"
#include "../Helpers/FS_Helper.h"
#include "../Helpers/Hardware_device_info.h"
#include "../Helpers/Hardware_GPIO.h"
+1
View File
@@ -16,6 +16,7 @@
#include "../Helpers/_CPlugin_init.h"
#include "../Helpers/ESPEasy_math.h"
#include "../Helpers/ESPEasy_Storage.h"
#include "../Helpers/ESPEasy_UnitOfMeasure.h"
#include "../Helpers/Misc.h"
#include "../Helpers/Numerical.h"
#include "../Helpers/StringConverter.h"
+1
View File
@@ -9,6 +9,7 @@
# include "../Helpers/StringGenerator_System.h"
# endif // if FEATURE_MQTT_DISCOVER
# include "../Helpers/SystemVariables.h"
# include "../Helpers/ESPEasy_UnitOfMeasure.h"
# ifdef USES_P001
# include "../PluginStructs/P001_data_struct.h"
@@ -5,6 +5,7 @@
# include "../DataStructs/ESPEasy_packed_raw_data.h"
# include "../ESPEasyCore/ESPEasyNetwork.h"
# include "../Globals/ESPEasyWiFiEvent.h"
# include "../Helpers/ESPEasy_UnitOfMeasure.h"
# include "../Helpers/Memory.h"
# include "../Helpers/Hardware_temperature_sensor.h"
# ifdef ESP32
@@ -4,6 +4,8 @@
#ifdef USES_P078
# include "../Helpers/ESPEasy_UnitOfMeasure.h"
# include <limits>
# include <SDM.h> // Requires SDM library from Reaper7 - https://github.com/reaper7/SDM_Energy_Meter/
@@ -247,6 +249,39 @@ Sensor_VType Plugin_078_QueryVType(SDM_MODEL model, int choice) {
# endif // if FEATURE_MQTT_DISCOVER
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
uint64_t Plugin_078_QueryUOMGroup(SDM_MODEL model, int choice) {
const int index = SDM_getRegisterDescriptionIndexForModel(model, choice);
if (index >= 0) {
const uint8_t value_nr = static_cast<uint8_t>(register_description_list[index].getUnitOfMeasure());
const uint64_t vtypes[] = {
UOM_GROUP_PERC_HUM_BAT_MOIST, // THD
UOM_GROUP_VOLTAGE_CURRENT, // Voltage
UOM_GROUP_VOLTAGE_CURRENT, // Current
UOM_GROUP_POWER, // Power
UOM_GROUP_ENERGY_STORAGE, // Active energy
UOM_GROUP_REACTIVE_POWER, // Ah
UOM_GROUP_FREQUENCY, // Frequency
UOM_GROUP_WIND_DIRECTION, // Phase angle
UOM_GROUP_PERC_HUM_BAT_MOIST, // Power factor
UOM_GROUP_APPARENT_POWER, // Apparant power
UOM_GROUP_REACTIVE_POWER, // Reactive power
UOM_GROUP_APPARENT_POWER, // Apparent energy
UOM_GROUP_REACTIVE_POWER, // Reactive energy
UOM_GROUP_NONE, // Nature of load
};
constexpr uint8_t valueCount = NR_ELEMENTS(vtypes);
if (value_nr < valueCount) {
return vtypes[value_nr];
}
}
return UOM_GROUP_ALL;
}
# endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
void SDM_loadOutputSelector(struct EventStruct *event, uint8_t pconfigIndex, uint8_t valuenr)
{
const SDM_MODEL model = static_cast<SDM_MODEL>(P078_MODEL);
+5 -1
View File
@@ -146,8 +146,12 @@ String SDM_getValueNameForModel(SDM_MODEL model,
int choice);
# if FEATURE_MQTT_DISCOVER
Sensor_VType Plugin_078_QueryVType(SDM_MODEL model,
int choice);
int choice);
# endif // if FEATURE_MQTT_DISCOVER
# if FEATURE_TASKVALUE_UNIT_OF_MEASURE
uint64_t Plugin_078_QueryUOMGroup(SDM_MODEL model,
int choice);
# endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
struct SDM_RegisterReadQueueElement {
SDM_RegisterReadQueueElement(taskIndex_t TaskIndex, taskVarIndex_t TaskVarIndex, uint16_t reg, uint8_t dev_id)
+65 -22
View File
@@ -38,6 +38,9 @@
# include <ESPeasySerial.h>
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
#include "../Helpers/ESPEasy_UnitOfMeasure.h"
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
void handle_devices() {
# ifndef BUILD_NO_RAM_TRACKER
@@ -1743,6 +1746,40 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
html_table_header(F("Unit of Measure"), 300);
++colCount;
EventStruct uomEvent(taskIndex);
String uomDummy;
bool limitedUom = PluginCall(DeviceIndex, PLUGIN_GET_UOM_GROUPS, &uomEvent, uomDummy);
if (!limitedUom) {
PluginCall(PLUGIN_GET_DEVICEVTYPE, &uomEvent, uomDummy); // Get Sensor_VType
limitedUom = getDefaultUoMforSensorVType(&uomEvent); // Populate UoM groups for known Sensor_VTypes
#if FEATURE_MQTT_DISCOVER && FEATURE_CUSTOM_TASKVAR_VTYPE
if (!limitedUom) {
// Fill in standard Unit of measurement and Value Type, if possible
std::vector<DiscoveryItem> discoveryItems;
MQTT_DiscoveryGetDeviceVType(taskIndex, discoveryItems, getValueCountForTask(taskIndex), uomDummy);
for (uint8_t varNr = 0; varNr < VARS_PER_TASK; ++varNr) {
// Match varNr with the DiscoveryItems to find the Sensor_VType for the value
for (uint8_t j = 0; j < discoveryItems.size(); ++j) {
for (uint8_t k = 0; k < discoveryItems[j].valueCount; ++k) { // Can have multiple values for 1 VType
if (varNr == discoveryItems[j].varIndex + k) {
const String uom = getValueType2DefaultHAUoM(discoveryItems[j].VType);
const uint16_t uomGroup = getUoMGroupForUoM(uom);
const uint16_t uomGroup2 = getUoMGroupForUoM(Cache.getTaskVarUnitOfMeasure(taskIndex, varNr));
if (uomGroup > 0) {
uomEvent.Par64N[varNr] = (uint64_t)(1ULL << (uomGroup - 1024)) | (uomGroup2 > 0 ? (uint64_t)(1ULL << (uomGroup2 - 1024)) : 0ULL);
limitedUom = true;
} else {
uomEvent.Par64N[varNr] = UOM_GROUP_ALL;
}
}
}
}
}
}
#endif // if FEATURE_MQTT_DISCOVER && FEATURE_CUSTOM_TASKVAR_VTYPE
}
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
#if FEATURE_CUSTOM_TASKVAR_VTYPE
@@ -1766,7 +1803,6 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde
#if FEATURE_CUSTOM_TASKVAR_VTYPE
std::vector<uint8_t> singleOptions;
EventStruct TempEvent(taskIndex);
if (device.CustomVTypeVar) {
// Build a list of all single-value available value VTypes from PR #5199
@@ -1780,6 +1816,31 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde
}
#endif // if FEATURE_CUSTOM_TASKVAR_VTYPE
# if FEATURE_PLUGIN_STATS
const __FlashStringHelper *chartAxis[] = {
F("L1"),
F("L2"),
F("L3"),
F("L4"),
F("R1"),
F("R2"),
F("R3"),
F("R4")
};
constexpr size_t chartAxisCount = NR_ELEMENTS(chartAxis);
#endif // if FEATURE_PLUGIN_STATS
#if FEATURE_MQTT_STATE_CLASS
const __FlashStringHelper *stateClasses[] = {
MQTT_sensor_StateClass(0),
MQTT_sensor_StateClass(1),
MQTT_sensor_StateClass(4),
MQTT_sensor_StateClass(2),
MQTT_sensor_StateClass(3),
};
constexpr size_t stateCount = NR_ELEMENTS(stateClasses);
#endif // if FEATURE_MQTT_STATE_CLASS
// table body
for (uint8_t varNr = 0; varNr < valueCount; varNr++)
{
@@ -1822,24 +1883,13 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde
html_TD();
const __FlashStringHelper *chartAxis[] = {
F("L1"),
F("L2"),
F("L3"),
F("L4"),
F("R1"),
F("R2"),
F("R3"),
F("R4")
};
int selected = cachedConfig.getAxisIndex();
if (!cachedConfig.isLeft()) {
selected += 4;
}
const FormSelectorOptions selector(NR_ELEMENTS(chartAxis), chartAxis);
const FormSelectorOptions selector(chartAxisCount, chartAxis);
selector.addSelector(
getPluginCustomArgName(F("TDSA"), varNr),
selected);
@@ -1848,7 +1898,8 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
html_TD();
addUnitOfMeasureSelector(getPluginCustomArgName(F("TUOM"), varNr), Cache.getTaskVarUnitOfMeasure(taskIndex, varNr));
const uint64_t uomGroups = limitedUom ? uomEvent.Par64N[varNr] : UOM_GROUP_ALL;
addUnitOfMeasureSelector(getPluginCustomArgName(F("TUOM"), varNr), Cache.getTaskVarUnitOfMeasure(taskIndex, varNr), uomGroups);
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
#if FEATURE_CUSTOM_TASKVAR_VTYPE
@@ -1865,15 +1916,7 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde
#if FEATURE_MQTT_STATE_CLASS
if (device.MqttStateClass) {
html_TD();
const __FlashStringHelper *stateClasses[] = {
MQTT_sensor_StateClass(0),
MQTT_sensor_StateClass(1),
MQTT_sensor_StateClass(4),
MQTT_sensor_StateClass(2),
MQTT_sensor_StateClass(3),
};
constexpr size_t stateCount = NR_ELEMENTS(stateClasses);
const FormSelectorOptions selectorSC(stateCount, stateClasses);
selectorSC.addSelector(
getPluginCustomArgName(F("TDSC"), varNr),
+1
View File
@@ -17,6 +17,7 @@
#include "../Helpers/_Plugin_init.h"
#include "../Helpers/ESPEasyStatistics.h"
#include "../Helpers/ESPEasy_Storage.h"
#include "../Helpers/ESPEasy_UnitOfMeasure.h"
#include "../Helpers/Numerical.h"
#include "../Helpers/StringConverter.h"
#include "../Helpers/StringProvider.h"
+20 -134
View File
@@ -9,6 +9,7 @@
#include "../Globals/Settings.h"
#include "../Helpers/Convert.h"
#include "../Helpers/ESPEasy_UnitOfMeasure.h"
#include "../Helpers/Hardware_GPIO.h"
#include "../Helpers/StringConverter_Numerical.h"
#include "../Helpers/StringConverter.h"
@@ -351,131 +352,11 @@ void addUnit(char unit)
}
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
const char unit_of_measure_list[] PROGMEM = // *** DO NOT CHANGE ORDER, SAVED IN TASK SETTINGS! ***
"|" // 0 = Empty/none
"°C|°F|K|" // 1..3
"%|" // 4
"Pa|hPa|bar|mbar|inHg|psi|" // 5..10
"W|kW|" // 11..12
"V|" // 13
"Wh|kWh|" // 14..15
"A|VA|" // 16..17
"mm|cm|m|km|" // 18..21
"L|mL|m³|ft³|" // 22..25
"m³/h|ft³/h|" // 26..27
"lx|" // 28
"UV index|" // 29
"µg/m³|mg/m³|p/m³|ppm|ppb|" // 30..34
"°|" // 35
"€|$|¢|" // 36..38
"μs|ms|s|min|h|d|w|m|y|" // 39..47
"in|ft|yd|mi|" // 48..51
"Hz|GHz|" // 52..53
"gal|fl. oz|" // 54..55
"m²|" // 56
"g|kg|mg|µg|" // 57..60
"oz|lb|" // 61..62
"µS/cm|" // 63
"W/m²|" // 64
"mm/h|" // 65
"mm/s|in/s|m/s|in/h|km/h|mph|" // 66..71
"db|dBm|" // 72..73
"bit|kbit|Mbit|Gbit|B|kB|MB|GB|TB|PB|EB|ZB|YB|KiB|MiB|GiB|TiB|PiB|EiB|ZiB|YiB|" // 74..94
"bit/s|kbit/s|Mbit/s|Gbit/s|B/s|kB/s|MB/s|GB/s|KiB/s|MiB/s|GiB/s|" // 95..105
"ft/s|kn|" // 106..107
"mW|MW|GW|TW|" // 108..111
"BTU/(h⋅ft²)|" // 112
"pH|" // 113
"cbar|mmHg|kPa|" // 114..116
"mA|µA|mV|µV|kV|" // 117..121
"cm²|km²|mm²|in²|ft²|yd²|mi²|ac|ha|" // 122..130
"kHz|MHz|" // 131..132
"mWh|MWh|GWh|TWh|cal|kcal|Mcal|Gcal|J|kJ|MJ|GJ|" // 133..144
"var|kvar|varh|kvarh|" // 145..148
"st|" // 149
"mg/dL|mmol/L|" // 150..151
"μSv|μSv/h|" // 152..153
"m³/s|ft³/min|L/h|L/min|L/s|gal/min|mL/s|" // 154..160
"g/m³|kWh/100km|Wh/km|mi/kWh|km/kWh|" // 161..165
"in/d|mm/d" // 166 .. 167
; // *** DO NOT CHANGE ORDER, SAVED IN TASK SETTINGS! ***
const char unit_of_measure_labels[] PROGMEM = // Not stored, when UoM index >= 1024 it's a label-index with 1024 subtracted
"Apparent power|Air quality/CO/CO2|Area|(Atmospheric) Pressure|" // A 1024..1027
"Blood glucose concentr.|" // B 1028
"Data rate|Data size|Distance|Duration|" // D 1029..1032
"Energy distance|Energy(-storage)|" // E 1033..1034
"Frequency|" // F 1035
"Gas|" // G 1036
"Percent Hum./Batt./Moist.|" // H 1037
"Illuminance|Irradiance|" // I 1038..1039
"Monetary|" // M 1040
"Nitrogen (di-/mon-)oxide|" // N 1041
"Voc/Ozone|" // O 1042
"Ph|PM/CO/CO2/NO(x)/Voc/Ozone|Power|" // P 1043..1045
"Radiation|Reactive energy/power|" // R 1046..1047
"Signal strength|Sound pressure|Speed|" // S 1048..1050
"Temperature|" // T 1051
"Voltage/Current|Volume/Water cons.|Volume flow rate|" // V 1052..1054
"Weight|Wind direction|" // W 1055..1056
"Various units|" // Additional 1057
;
const uint16_t unit_of_measure_map[] PROGMEM = {
1051, 1, 2, 3, // Temperature
1037, 4, // Percent Battery, Humidity, Moisture
1027, 8, 6, 116, 7, 115, 10, 5, 114, 9, // (Atmospheric) Pressure
1052, 13, 119, 120, 121, 16, 117, 118, // Voltage/Current
1045, 11, 12, 108, 109, 110, 111, // Power
1024, 17, // Apparent power
1047, 145, 146, 147, 148, // Reactive power/energy
1044, 30, 31, 32, 33, 34, // Particle matter
1031, 18, 19, 20, 21, 48, 49, 50, 51, // Distance
1055, 57, 58, 59, 60, 61, 62, 149, // Weight
1053, 22, 23, 24, 25, 54, 55, // Volume/Water
1054, 26, 27, 153, 154, 155, 156, 157, 158, 159, 160, // Volume flow rate
1032, 39, 40, 41, 42, 43, 44, 45, 46, 47, // Duration
1034, 14, 15, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, // Energy(-storage)
1033, 162, 163, 164, 165, // Energy distance
1050, 66, 67, 68, 69, 70, 71, 65, 106, 107, // Speed
1056, 35, // (Wind) direction
1038, 28, // Illuminance
1039, 64, 112, // Irradiance
1046, 152, 153, // Radiation
1057, 29, 63, 161, 166, 167, // Various units
1035, 52, 53, 131, 132, // Frequency
1043, 113, // Potential hydrogen
1026, 56, 122, 123, 124, 125, 126, 127, 128, 129, 130, // Area
1029, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, // Data rate
1030, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, // Data size
1049, 72, 73, // Sound pressure
1028, 150, 151, // Blood glucose
1040, 36, 37, 38, // Monetary
};
String toUnitOfMeasureName(const uint32_t unitOfMeasureIndex,
const String & defUoM) {
char tmp[26]{};
String result;
if (unitOfMeasureIndex < 1024) {
result = GetTextIndexed(tmp, sizeof(tmp), unitOfMeasureIndex, unit_of_measure_list);
} else {
result = GetTextIndexed(tmp, sizeof(tmp), unitOfMeasureIndex - 1024, unit_of_measure_labels);
}
return result.isEmpty() ? defUoM : result;
}
int getUnitOfMeasureIndex(const String& uomName) {
return GetCommandCode(uomName.c_str(), unit_of_measure_list);
}
void addUnitOfMeasureSelector(const String& id,
const uint8_t unitOfMeasure) {
constexpr uint16_t asize = NR_ELEMENTS(unit_of_measure_map);
bool firstGrp = true;
void addUnitOfMeasureSelector(const String& id,
const uint8_t unitOfMeasure,
const uint64_t groupMap) {
bool firstGrp = true;
bool includeGrp = false;
do_addSelector_Head(id, F("xwide"), EMPTY_STRING, false
#if FEATURE_TOOLTIPS
@@ -487,19 +368,24 @@ void addUnitOfMeasureSelector(const String& id,
0,
unitOfMeasure == 0);
for (uint16_t idx = 0; idx < asize; ++idx) {
for (uint16_t idx = 0; idx < unit_of_measure_map_size; ++idx) {
const uint16_t uomIdx = pgm_read_word_near(&unit_of_measure_map[idx]);
if (uomIdx < 1024) {
addSelector_Item(
toUnitOfMeasureName(uomIdx),
uomIdx,
unitOfMeasure == uomIdx);
if (includeGrp) {
addSelector_Item(
toUnitOfMeasureName(uomIdx),
uomIdx,
unitOfMeasure == uomIdx);
}
} else {
if (!firstGrp) {
addSelector_OptGroupFoot();
includeGrp = bitRead(groupMap, uomIdx - 1024);
if (includeGrp) {
if (!firstGrp) {
addSelector_OptGroupFoot();
}
addSelector_OptGroup(toUnitOfMeasureName(uomIdx));
firstGrp = false;
}
addSelector_OptGroup(toUnitOfMeasureName(uomIdx));
firstGrp = false;
}
if ((idx & 0x07) == 0) { delay(0); }
}
+3 -5
View File
@@ -107,11 +107,9 @@ void addUnit(const String& unit);
void addUnit(char unit);
#if FEATURE_TASKVALUE_UNIT_OF_MEASURE
String toUnitOfMeasureName(const uint32_t unitOfMeasureIndex,
const String & defUoM = EMPTY_STRING);
int getUnitOfMeasureIndex(const String& uomName);
void addUnitOfMeasureSelector(const String& id,
const uint8_t unitOfMeasure);
void addUnitOfMeasureSelector(const String& id,
const uint8_t unitOfMeasure,
const uint64_t groupMap);
#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE
void addRowLabel_tr_id(const __FlashStringHelper *label,