Render values in Web natively #70

This commit is contained in:
proddy
2021-06-07 21:19:52 +02:00
parent db43f2d711
commit 8dd18aa24d
23 changed files with 946 additions and 724 deletions

View File

@@ -39,7 +39,8 @@ static const __FlashStringHelper * DeviceValueUOM_s[] __attribute__((__aligned__
F_(kb),
F_(seconds),
F_(dbm),
F_(num)
F_(num),
F_(bool)
};
@@ -543,7 +544,7 @@ std::string EMSdevice::get_value_uom(const char * key) {
for (const auto & dv : devicevalues_) {
if (dv.full_name != nullptr) {
if (uuid::read_flash_string(dv.full_name) == p) {
// ignore TIME since "minutes" is already included
// ignore TIME since "minutes" is already added to the string value
if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) {
break;
}
@@ -555,37 +556,34 @@ std::string EMSdevice::get_value_uom(const char * key) {
return std::string{}; // not found
}
// prepare array of device values, as 3 elements serialized (name, value, uom) in array to send to Web UI
// returns number of elements
bool EMSdevice::generate_values_json_web(JsonObject & json) {
// prepare array of device values used for the Web UI
// v = value, u=uom, n=name, c=cmd
void EMSdevice::generate_values_json_web(JsonObject & json) {
json["name"] = to_string_short();
JsonArray data = json.createNestedArray("data");
uint8_t num_elements = 0;
for (const auto & dv : devicevalues_) {
// ignore if full_name empty
if (dv.full_name != nullptr) {
JsonObject obj; // create the object, if needed
// handle Booleans (true, false)
if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
// see if we have options for the bool's
if (dv.options_size == 2) {
data.add(*(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1]);
} else {
// always render booleans as on or off
data.add(*(uint8_t *)(dv.value_p) ? F_(on) : F_(off));
}
obj = data.createNestedObject();
obj["v"] = *(bool *)(dv.value_p);
}
// handle TEXT strings
else if ((dv.type == DeviceValueType::TEXT) && (Helpers::hasValue((char *)(dv.value_p)))) {
data.add((char *)(dv.value_p));
obj = data.createNestedObject();
obj["v"] = (char *)(dv.value_p);
}
// handle ENUMs
else if ((dv.type == DeviceValueType::ENUM) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
if (*(uint8_t *)(dv.value_p) < dv.options_size) {
data.add(dv.options[*(uint8_t *)(dv.value_p)]);
obj = data.createNestedObject();
obj["v"] = dv.options[*(uint8_t *)(dv.value_p)];
}
}
@@ -596,83 +594,55 @@ bool EMSdevice::generate_values_json_web(JsonObject & json) {
// the nested if's is necessary due to the way the ArduinoJson templates are pre-processed by the compiler
uint8_t divider = (dv.options_size == 1) ? Helpers::atoint(uuid::read_flash_string(dv.options[0]).c_str()) : 0;
// INT
if ((dv.type == DeviceValueType::INT) && Helpers::hasValue(*(int8_t *)(dv.value_p))) {
if (divider) {
data.add(Helpers::round2(*(int8_t *)(dv.value_p), divider));
} else {
data.add(*(int8_t *)(dv.value_p));
}
obj = data.createNestedObject();
obj["v"] = (divider) ? Helpers::round2(*(int8_t *)(dv.value_p), divider) : *(int8_t *)(dv.value_p);
} else if ((dv.type == DeviceValueType::UINT) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
if (divider) {
data.add(Helpers::round2(*(uint8_t *)(dv.value_p), divider));
} else {
data.add(*(uint8_t *)(dv.value_p));
}
obj = data.createNestedObject();
obj["v"] = (divider) ? Helpers::round2(*(uint8_t *)(dv.value_p), divider) : *(uint8_t *)(dv.value_p);
} else if ((dv.type == DeviceValueType::SHORT) && Helpers::hasValue(*(int16_t *)(dv.value_p))) {
if (divider) {
data.add(Helpers::round2(*(int16_t *)(dv.value_p), divider));
} else {
data.add(*(int16_t *)(dv.value_p));
}
obj = data.createNestedObject();
obj["v"] = (divider) ? Helpers::round2(*(int16_t *)(dv.value_p), divider) : *(int16_t *)(dv.value_p);
} else if ((dv.type == DeviceValueType::USHORT) && Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
if (divider) {
data.add(Helpers::round2(*(uint16_t *)(dv.value_p), divider));
} else {
data.add(*(uint16_t *)(dv.value_p));
}
obj = data.createNestedObject();
obj["v"] = (divider) ? Helpers::round2(*(uint16_t *)(dv.value_p), divider) : *(uint16_t *)(dv.value_p);
} else if ((dv.type == DeviceValueType::ULONG) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
if (divider) {
data.add(Helpers::round2(*(uint32_t *)(dv.value_p), divider));
} else {
data.add(*(uint32_t *)(dv.value_p));
}
obj = data.createNestedObject();
obj["v"] = (divider) ? Helpers::round2(*(uint32_t *)(dv.value_p), divider) : *(uint32_t *)(dv.value_p);
} else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
uint32_t time_value = *(uint32_t *)(dv.value_p);
time_value = (divider) ? time_value / divider : time_value; // sometimes we need to divide by 60
char time_s[40];
snprintf_P(time_s, 40, PSTR("%d days %d hours %d minutes"), (time_value / 1440), ((time_value % 1440) / 60), (time_value % 60));
data.add(time_s);
obj = data.createNestedObject();
obj["v"] = (divider) ? time_value / divider : time_value; // sometimes we need to divide by 60
}
}
// check if we've added a data element by comparing the size
// then add the remaining elements
uint8_t sz = data.size();
if (sz > num_elements) {
// check if we've added a data element then add the remaining elements
if (obj.containsKey("v")) {
// add the unit of measure (uom)
if (dv.uom == DeviceValueUOM::MINUTES) {
data.add(nullptr); // use null for time/date
} else {
data.add(uom_to_string(dv.uom));
}
obj["u"] = dv.uom;
// add name, prefixing the tag if it exists
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
data.add(dv.full_name);
obj["n"] = dv.full_name;
} else {
char name[50];
snprintf_P(name, sizeof(name), "(%s) %s", tag_to_string(dv.tag).c_str(), uuid::read_flash_string(dv.full_name).c_str());
data.add(name);
obj["n"] = name;
}
// add the name of the Command function if it exists
if (dv.has_cmd) {
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
data.add(tag_to_string(dv.tag) + "/" + uuid::read_flash_string(dv.short_name));
obj["c"] = tag_to_string(dv.tag) + "/" + uuid::read_flash_string(dv.short_name);
} else {
data.add(dv.short_name);
obj["c"] = dv.short_name;
}
} else {
data.add("");
obj["c"] = "";
}
num_elements = sz + 3; // increase count by 3
}
}
}
return (num_elements != 0);
}
bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t id) {
@@ -778,9 +748,7 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
break;
case DeviceValueType::BOOL: {
if (Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
if (dv.options_size == 2) {
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? dv.options[0] : dv.options[1];
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(on) : F_(off);
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
json[value] = (bool)(*(uint8_t *)(dv.value_p)) ? F_(ON) : F_(OFF);
@@ -794,10 +762,8 @@ bool EMSdevice::get_value_info(JsonObject & root, const char * cmd, const int8_t
json[min] = 0;
json[max] = 1;
JsonArray enum_ = json.createNestedArray(F_(enum));
if (dv.options_size == 2) {
enum_.add(dv.options[1]);
enum_.add(dv.options[0]);
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF) {
enum_.add(F_(off));
enum_.add(F_(on));
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
@@ -885,31 +851,26 @@ bool EMSdevice::generate_values_json(JsonObject & root, const uint8_t tag_filter
// handle Booleans (true, false)
if ((dv.type == DeviceValueType::BOOL) && Helpers::hasValue(*(uint8_t *)(dv.value_p), EMS_VALUE_BOOL)) {
// see if we have options for the bool's
if (dv.options_size == 2 && Mqtt::bool_format() != BOOL_FORMAT_10) {
json[name] = *(uint8_t *)(dv.value_p) ? dv.options[0] : dv.options[1];
// see how to render the value depending on the setting
// when in console mode we always use on and off
if ((Mqtt::bool_format() == BOOL_FORMAT_ONOFF) || console) {
// on or off as strings
json[name] = *(uint8_t *)(dv.value_p) ? F_(on) : F_(off);
has_value = true;
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
// on or off as strings
json[name] = *(uint8_t *)(dv.value_p) ? F_(ON) : F_(OFF);
has_value = true;
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
// true or false values (as real booleans, not strings)
json[name] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false;
has_value = true;
} else {
// see how to render the value depending on the setting
// when in console mode we always use on and off
if ((Mqtt::bool_format() == BOOL_FORMAT_ONOFF) || console) {
// on or off as strings
json[name] = *(uint8_t *)(dv.value_p) ? F_(on) : F_(off);
has_value = true;
} else if (Mqtt::bool_format() == BOOL_FORMAT_ONOFF_CAP) {
// on or off as strings
json[name] = *(uint8_t *)(dv.value_p) ? F_(ON) : F_(OFF);
has_value = true;
} else if (Mqtt::bool_format() == BOOL_FORMAT_TRUEFALSE) {
// true or false values (as real booleans, not strings)
json[name] = (bool)(*(uint8_t *)(dv.value_p)) ? true : false;
has_value = true;
} else {
// numerical 1 or 0
json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)) ? 1 : 0;
has_value = true;
}
// numerical 1 or 0
json[name] = (uint8_t)(*(uint8_t *)(dv.value_p)) ? 1 : 0;
has_value = true;
}
}
// handle TEXT strings