fix devicevalue sorting the Michael way (thanks)

This commit is contained in:
proddy
2022-03-28 17:50:33 +02:00
parent bad6346e7a
commit 592c5ca778

View File

@@ -637,119 +637,116 @@ void EMSdevice::generate_values_web(JsonObject & output) {
output["label"] = to_string_short(); output["label"] = to_string_short();
JsonArray data = output.createNestedArray("data"); JsonArray data = output.createNestedArray("data");
// sort the device values // do two passes. First for all entities marked as favourites, then for all others. This sorts the list.
std::sort(devicevalues_.begin(), devicevalues_.end(), [](const emsesp::DeviceValue & a, const emsesp::DeviceValue & b __attribute__((unused))) { for (uint8_t i = 0; i < 2; i++) {
return a.has_state(DeviceValueState::DV_FAVORITE); for (auto & dv : devicevalues_) {
}); bool state = (!i && dv.has_state(DeviceValueState::DV_FAVORITE)) || (i && !dv.has_state(DeviceValueState::DV_FAVORITE));
if (state && (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.full_name && (dv.hasValue() || (dv.type == DeviceValueType::CMD)))) {
JsonObject obj = data.createNestedObject(); // create the object, we know there is a value
uint8_t fahrenheit = 0;
for (auto & dv : devicevalues_) { // handle Booleans (true, false)
// check conditions: if (dv.type == DeviceValueType::BOOL) {
// 1. full_name cannot be empty bool value_b = *(bool *)(dv.value_p);
// 2. it must have a valid value, if it is not a command like 'reset' if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) {
obj["v"] = value_b ? "true" : "false";
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) {
obj["v"] = value_b ? 1 : 0;
} else {
char s[7];
obj["v"] = Helpers::render_boolean(s, value_b);
}
}
if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.full_name && (dv.hasValue() || (dv.type == DeviceValueType::CMD))) { // handle TEXT strings
JsonObject obj = data.createNestedObject(); // create the object, we know there is a value else if (dv.type == DeviceValueType::STRING) {
uint8_t fahrenheit = 0; obj["v"] = (char *)(dv.value_p);
}
// handle Booleans (true, false) // handle ENUMs
if (dv.type == DeviceValueType::BOOL) { else if ((dv.type == DeviceValueType::ENUM) && (*(uint8_t *)(dv.value_p) < dv.options_size)) {
bool value_b = *(bool *)(dv.value_p); obj["v"] = dv.options[*(uint8_t *)(dv.value_p)];
if (EMSESP::system_.bool_format() == BOOL_FORMAT_TRUEFALSE) { }
obj["v"] = value_b ? "true" : "false";
} else if (EMSESP::system_.bool_format() == BOOL_FORMAT_10) { // handle numbers
obj["v"] = value_b ? 1 : 0; else {
// If a divider is specified, do the division to 2 decimals places and send back as double/float
// otherwise force as an integer whole
// the nested if's is necessary due to the way the ArduinoJson templates are pre-processed by the compiler
int8_t divider = (dv.options_size == 1) ? Helpers::atoint(read_flash_string(dv.options[0]).c_str()) : 0;
fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
if ((dv.type == DeviceValueType::INT) && Helpers::hasValue(*(int8_t *)(dv.value_p))) {
obj["v"] = Helpers::round2(*(int8_t *)(dv.value_p), divider, fahrenheit);
} else if ((dv.type == DeviceValueType::UINT) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
obj["v"] = Helpers::round2(*(uint8_t *)(dv.value_p), divider, fahrenheit);
} else if ((dv.type == DeviceValueType::SHORT) && Helpers::hasValue(*(int16_t *)(dv.value_p))) {
obj["v"] = Helpers::round2(*(int16_t *)(dv.value_p), divider, fahrenheit);
} else if ((dv.type == DeviceValueType::USHORT) && Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
obj["v"] = Helpers::round2(*(uint16_t *)(dv.value_p), divider, fahrenheit);
} else if ((dv.type == DeviceValueType::ULONG) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
obj["v"] = Helpers::round2(*(uint32_t *)(dv.value_p), divider, fahrenheit);
} else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
uint32_t time_value = *(uint32_t *)(dv.value_p);
obj["v"] = (divider > 0) ? time_value / divider : time_value; // sometimes we need to divide by 60
}
}
// add the unit of measure (uom)
obj["u"] = fahrenheit ? (uint8_t)DeviceValueUOM::FAHRENHEIT : dv.uom;
auto mask = Helpers::hextoa((uint8_t)(dv.state >> 4), false); // create mask to a 2-char string
// add name, prefixing the tag if it exists
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
obj["n"] = mask + read_flash_string(dv.full_name);
} else if (dv.tag < DeviceValueTAG::TAG_HC1) {
obj["n"] = mask + tag_to_string(dv.tag) + " " + read_flash_string(dv.full_name);
} else { } else {
char s[7]; obj["n"] = mask + tag_to_string(dv.tag) + " " + read_flash_string(dv.full_name);
obj["v"] = Helpers::render_boolean(s, value_b);
} }
}
// handle TEXT strings // add commands and options
else if (dv.type == DeviceValueType::STRING) { if (dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY)) {
obj["v"] = (char *)(dv.value_p); // add the name of the Command function
} if (dv.tag >= DeviceValueTAG::TAG_HC1) {
obj["c"] = tag_to_mqtt(dv.tag) + "/" + read_flash_string(dv.short_name);
// handle ENUMs } else {
else if ((dv.type == DeviceValueType::ENUM) && (*(uint8_t *)(dv.value_p) < dv.options_size)) { obj["c"] = dv.short_name;
obj["v"] = dv.options[*(uint8_t *)(dv.value_p)]; }
} // add the Command options
if (dv.type == DeviceValueType::ENUM || (dv.type == DeviceValueType::CMD && dv.options_size > 1)) {
// handle numbers JsonArray l = obj.createNestedArray("l");
else { for (uint8_t i = 0; i < dv.options_size; i++) {
// If a divider is specified, do the division to 2 decimals places and send back as double/float if (!read_flash_string(dv.options[i]).empty()) {
// otherwise force as an integer whole l.add(read_flash_string(dv.options[i]));
// the nested if's is necessary due to the way the ArduinoJson templates are pre-processed by the compiler }
int8_t divider = (dv.options_size == 1) ? Helpers::atoint(read_flash_string(dv.options[0]).c_str()) : 0; }
fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0; } else if (dv.type == DeviceValueType::BOOL) {
JsonArray l = obj.createNestedArray("l");
if ((dv.type == DeviceValueType::INT) && Helpers::hasValue(*(int8_t *)(dv.value_p))) { l.add("off");
obj["v"] = Helpers::round2(*(int8_t *)(dv.value_p), divider, fahrenheit); l.add("on");
} else if ((dv.type == DeviceValueType::UINT) && Helpers::hasValue(*(uint8_t *)(dv.value_p))) { }
obj["v"] = Helpers::round2(*(uint8_t *)(dv.value_p), divider, fahrenheit); // add command help template
} else if ((dv.type == DeviceValueType::SHORT) && Helpers::hasValue(*(int16_t *)(dv.value_p))) { else if (dv.type == DeviceValueType::STRING || dv.type == DeviceValueType::CMD) {
obj["v"] = Helpers::round2(*(int16_t *)(dv.value_p), divider, fahrenheit); if (dv.options_size == 1) {
} else if ((dv.type == DeviceValueType::USHORT) && Helpers::hasValue(*(uint16_t *)(dv.value_p))) { obj["h"] = dv.options[0];
obj["v"] = Helpers::round2(*(uint16_t *)(dv.value_p), divider, fahrenheit);
} else if ((dv.type == DeviceValueType::ULONG) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
obj["v"] = Helpers::round2(*(uint32_t *)(dv.value_p), divider, fahrenheit);
} else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
uint32_t time_value = *(uint32_t *)(dv.value_p);
obj["v"] = (divider > 0) ? time_value / divider : time_value; // sometimes we need to divide by 60
}
}
// add the unit of measure (uom)
obj["u"] = fahrenheit ? (uint8_t)DeviceValueUOM::FAHRENHEIT : dv.uom;
// add name, prefixing the tag if it exists
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
obj["n"] = dv.full_name;
} else if (dv.tag < DeviceValueTAG::TAG_HC1) {
obj["n"] = tag_to_string(dv.tag) + " " + read_flash_string(dv.full_name);
} else {
obj["n"] = tag_to_string(dv.tag) + " " + read_flash_string(dv.full_name);
}
// add commands and options
if (dv.has_cmd && !dv.has_state(DeviceValueState::DV_READONLY)) {
// add the name of the Command function
if (dv.tag >= DeviceValueTAG::TAG_HC1) {
obj["c"] = tag_to_mqtt(dv.tag) + "/" + read_flash_string(dv.short_name);
} else {
obj["c"] = dv.short_name;
}
// add the Command options
if (dv.type == DeviceValueType::ENUM || (dv.type == DeviceValueType::CMD && dv.options_size > 1)) {
JsonArray l = obj.createNestedArray("l");
for (uint8_t i = 0; i < dv.options_size; i++) {
if (!read_flash_string(dv.options[i]).empty()) {
l.add(read_flash_string(dv.options[i]));
} }
} }
} else if (dv.type == DeviceValueType::BOOL) { // add steps to numeric values with divider/multiplier
JsonArray l = obj.createNestedArray("l"); else {
l.add("off"); int8_t divider = (dv.options_size == 1) ? Helpers::atoint(read_flash_string(dv.options[0]).c_str()) : 0;
l.add("on"); char s[10];
} if (divider > 0) {
// add command help template obj["s"] = Helpers::render_value(s, (float)1 / divider, 1);
else if (dv.type == DeviceValueType::STRING || dv.type == DeviceValueType::CMD) { } else if (divider < 0) {
if (dv.options_size == 1) { obj["s"] = Helpers::render_value(s, (-1) * divider, 0);
obj["h"] = dv.options[0]; }
} int16_t dv_set_min, dv_set_max;
} if (dv.get_min_max(dv_set_min, dv_set_max)) {
// add steps to numeric values with divider/multiplier obj["m"] = Helpers::render_value(s, dv_set_min, 0);
else { obj["x"] = Helpers::render_value(s, dv_set_max, 0);
int8_t divider = (dv.options_size == 1) ? Helpers::atoint(read_flash_string(dv.options[0]).c_str()) : 0; }
char s[10];
if (divider > 0) {
obj["s"] = Helpers::render_value(s, (float)1 / divider, 1);
} else if (divider < 0) {
obj["s"] = Helpers::render_value(s, (-1) * divider, 0);
}
int16_t dv_set_min, dv_set_max;
if (dv.get_min_max(dv_set_min, dv_set_max)) {
obj["m"] = Helpers::render_value(s, dv_set_min, 0);
obj["x"] = Helpers::render_value(s, dv_set_max, 0);
} }
} }
} }
@@ -853,7 +850,7 @@ void EMSdevice::generate_values_web_all(JsonArray & output) {
// this is called before loading in the exclude entities list from the customization service // this is called before loading in the exclude entities list from the customization service
void EMSdevice::reset_entity_masks() { void EMSdevice::reset_entity_masks() {
for (auto & dv : devicevalues_) { for (auto & dv : devicevalues_) {
dv.state &= 0x0F; dv.state &= 0x0F; // clear high nibble
} }
} }