mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
Multi-language/I18n support #22
This commit is contained in:
@@ -51,6 +51,7 @@ std::string EMSdevice::tag_to_mqtt(uint8_t tag) {
|
||||
return read_flash_string(DeviceValue::DeviceValueTAG_mqtt[tag]);
|
||||
}
|
||||
|
||||
// convert UOM to a string - these don't need translating
|
||||
std::string EMSdevice::uom_to_string(uint8_t uom) {
|
||||
if (EMSESP::system_.fahrenheit() && (uom == DeviceValueUOM::DEGREES || uom == DeviceValueUOM::DEGREES_R)) {
|
||||
return read_flash_string(DeviceValue::DeviceValueUOM_s[DeviceValueUOM::FAHRENHEIT]);
|
||||
@@ -107,7 +108,7 @@ std::string EMSdevice::device_type_2_device_name(const uint8_t device_type) {
|
||||
case DeviceType::GATEWAY:
|
||||
return read_flash_string(F_(gateway));
|
||||
default:
|
||||
return read_flash_string(F_(unknown));
|
||||
return Helpers::translated_word(FL_(unknown));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +259,8 @@ bool EMSdevice::has_tag(const uint8_t tag) const {
|
||||
// called from the command 'entities'
|
||||
void EMSdevice::list_device_entries(JsonObject & output) const {
|
||||
for (const auto & dv : devicevalues_) {
|
||||
if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.type != DeviceValueType::CMD && dv.full_name) {
|
||||
auto fullname = Helpers::translated_fword(dv.fullname);
|
||||
if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.type != DeviceValueType::CMD && fullname) {
|
||||
// if we have a tag prefix it
|
||||
char key[50];
|
||||
if (!EMSdevice::tag_to_mqtt(dv.tag).empty()) {
|
||||
@@ -270,7 +272,7 @@ void EMSdevice::list_device_entries(JsonObject & output) const {
|
||||
JsonArray details = output.createNestedArray(key);
|
||||
|
||||
// add the full name description
|
||||
details.add(dv.full_name);
|
||||
details.add(fullname);
|
||||
|
||||
// add uom
|
||||
if (!uom_to_string(dv.uom).empty() && uom_to_string(dv.uom) != " ") {
|
||||
@@ -386,23 +388,31 @@ void EMSdevice::register_telegram_type(const uint16_t telegram_type_id, const __
|
||||
// tag: to be used to group mqtt together, either as separate topics as a nested object
|
||||
// value_p: pointer to the value from the .h file
|
||||
// type: one of DeviceValueType
|
||||
// options: options for enum or a divider for int (e.g. F("10"))
|
||||
// options: options for enum, which are translated as a list of lists
|
||||
// options_single: list of names
|
||||
// numeric_operatpr: to divide or multiply, see DeviceValueNumOps::
|
||||
// short_name: used in Mqtt as keys
|
||||
// full_name: used in Web and Console unless empty (nullptr)
|
||||
// fullname: used in Web and Console unless empty (nullptr) - can be translated
|
||||
// uom: unit of measure from DeviceValueUOM
|
||||
// has_cmd: true if this is an associated command
|
||||
// min: min allowed value
|
||||
// max: max allowed value
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * short_name,
|
||||
const __FlashStringHelper * full_name,
|
||||
uint8_t uom,
|
||||
bool has_cmd,
|
||||
int16_t min,
|
||||
uint16_t max) {
|
||||
void EMSdevice::add_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const ** options,
|
||||
const __FlashStringHelper * const * options_single,
|
||||
int8_t numeric_operator,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint16_t max) {
|
||||
bool has_cmd = (f != nullptr);
|
||||
|
||||
auto short_name = name[0];
|
||||
const class __FlashStringHelper * const * fullname = &name[1]; // translations start at index 1
|
||||
|
||||
// initialize the device value depending on it's type
|
||||
if (type == DeviceValueType::STRING) {
|
||||
*(char *)(value_p) = {'\0'}; // this is important for string functions like strlen() to work later
|
||||
@@ -420,15 +430,6 @@ void EMSdevice::register_device_value(uint8_t tag,
|
||||
*(uint8_t *)(value_p) = EMS_VALUE_UINT_NOTSET; // enums behave as uint8_t
|
||||
}
|
||||
|
||||
// count #options
|
||||
uint8_t options_size = 0;
|
||||
if (options != nullptr) {
|
||||
uint8_t i = 0;
|
||||
while (options[i++]) {
|
||||
options_size++;
|
||||
}
|
||||
}
|
||||
|
||||
// determine state
|
||||
uint8_t state = DeviceValueState::DV_DEFAULT;
|
||||
|
||||
@@ -448,28 +449,11 @@ void EMSdevice::register_device_value(uint8_t tag,
|
||||
}
|
||||
});
|
||||
|
||||
// add the device
|
||||
devicevalues_.emplace_back(device_type_, tag, value_p, type, options, options_size, short_name, full_name, uom, 0, has_cmd, min, max, state);
|
||||
}
|
||||
|
||||
// function with min and max values
|
||||
// adds a new command to the command list
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint16_t max) {
|
||||
auto short_name = name[0];
|
||||
auto full_name = name[1];
|
||||
|
||||
register_device_value(tag, value_p, type, options, short_name, full_name, uom, (f != nullptr), min, max);
|
||||
// add the device entity
|
||||
devicevalues_.emplace_back(device_type_, tag, value_p, type, options, options_single, numeric_operator, short_name, fullname, uom, has_cmd, min, max, state);
|
||||
|
||||
// add a new command if it has a function attached
|
||||
if (f == nullptr) {
|
||||
if (!has_cmd) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -484,29 +468,109 @@ void EMSdevice::register_device_value(uint8_t tag,
|
||||
}
|
||||
|
||||
// add the command to our library
|
||||
// cmd is the short_name and the description is the full_name
|
||||
Command::add(device_type_, short_name, f, full_name, flags);
|
||||
// cmd is the short_name and the description is the fullname
|
||||
Command::add(device_type_, short_name, f, Helpers::translated_fword(fullname), flags);
|
||||
}
|
||||
|
||||
// function with no min and max values (set to 0)
|
||||
// single list of options
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * const * options_single,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f) {
|
||||
register_device_value(tag, value_p, type, options, name, uom, f, 0, 0);
|
||||
}
|
||||
// create a multi-list from the options
|
||||
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, 0, 0);
|
||||
};
|
||||
|
||||
// no associated command function, or min/max values
|
||||
// single list of options, with no translations, with min and max
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * options,
|
||||
const __FlashStringHelper * const * options_single,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom) {
|
||||
register_device_value(tag, value_p, type, options, name, uom, nullptr, 0, 0);
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint16_t max) {
|
||||
// create a multi-list from the options
|
||||
add_device_value(tag, value_p, type, nullptr, options_single, 0, name, uom, f, min, max);
|
||||
};
|
||||
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
int8_t numeric_operator,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f) {
|
||||
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, 0, 0);
|
||||
}
|
||||
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
int8_t numeric_operator,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint16_t max) {
|
||||
add_device_value(tag, value_p, type, nullptr, nullptr, numeric_operator, name, uom, f, min, max);
|
||||
}
|
||||
|
||||
// no options, no function
|
||||
void EMSdevice::register_device_value(uint8_t tag, void * value_p, uint8_t type, const __FlashStringHelper * const * name, uint8_t uom, const cmd_function_p f) {
|
||||
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, 0, 0);
|
||||
};
|
||||
|
||||
// no options, with min/max
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint16_t max) {
|
||||
add_device_value(tag, value_p, type, nullptr, nullptr, 0, name, uom, f, min, max);
|
||||
};
|
||||
|
||||
// function with min and max values
|
||||
// adds a new command to the command list
|
||||
// in this function we separate out the short and long names and take any translations
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const ** options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f,
|
||||
int16_t min,
|
||||
uint16_t max) {
|
||||
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, f, min, max);
|
||||
}
|
||||
|
||||
// function with no min and max values (set to 0)
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const ** options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom,
|
||||
const cmd_function_p f) {
|
||||
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, f, 0, 0);
|
||||
}
|
||||
|
||||
// no associated command function, or min/max values
|
||||
void EMSdevice::register_device_value(uint8_t tag,
|
||||
void * value_p,
|
||||
uint8_t type,
|
||||
const __FlashStringHelper * const ** options,
|
||||
const __FlashStringHelper * const * name,
|
||||
uint8_t uom) {
|
||||
add_device_value(tag, value_p, type, options, nullptr, 0, name, uom, nullptr, 0, 0);
|
||||
}
|
||||
|
||||
// check if value is readable via mqtt/api
|
||||
@@ -572,7 +636,8 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
snprintf(topic, sizeof(topic), "%s/%s", Mqtt::tag_to_topic(device_type_, dv.tag).c_str(), read_flash_string(dv.short_name).c_str());
|
||||
}
|
||||
|
||||
int8_t divider = (dv.options_size == 1) ? Helpers::atoint(read_flash_string(dv.options[0]).c_str()) : 0;
|
||||
int8_t num_op = dv.numeric_operator;
|
||||
|
||||
char payload[55] = {'\0'};
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
|
||||
@@ -585,32 +650,32 @@ void EMSdevice::publish_value(void * value_p) const {
|
||||
if (EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX) {
|
||||
Helpers::render_value(payload, *(uint8_t *)(value_p), 0);
|
||||
} else {
|
||||
strlcpy(payload, read_flash_string(dv.options[*(uint8_t *)(value_p)]).c_str(), sizeof(payload));
|
||||
auto enum_str = Helpers::translated_word(dv.options[*(uint8_t *)(value_p)]);
|
||||
strlcpy(payload, enum_str.c_str(), sizeof(payload));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DeviceValueType::USHORT:
|
||||
Helpers::render_value(payload, *(uint16_t *)(value_p), divider, fahrenheit);
|
||||
Helpers::render_value(payload, *(uint16_t *)(value_p), num_op, fahrenheit);
|
||||
break;
|
||||
case DeviceValueType::UINT:
|
||||
Helpers::render_value(payload, *(uint8_t *)(value_p), divider, fahrenheit);
|
||||
Helpers::render_value(payload, *(uint8_t *)(value_p), num_op, fahrenheit);
|
||||
break;
|
||||
case DeviceValueType::SHORT:
|
||||
Helpers::render_value(payload, *(int16_t *)(value_p), divider, fahrenheit);
|
||||
Helpers::render_value(payload, *(int16_t *)(value_p), num_op, fahrenheit);
|
||||
break;
|
||||
case DeviceValueType::INT:
|
||||
Helpers::render_value(payload, *(int8_t *)(value_p), divider, fahrenheit);
|
||||
Helpers::render_value(payload, *(int8_t *)(value_p), num_op, fahrenheit);
|
||||
break;
|
||||
case DeviceValueType::ULONG:
|
||||
Helpers::render_value(payload, *(uint32_t *)(value_p), divider, fahrenheit);
|
||||
Helpers::render_value(payload, *(uint32_t *)(value_p), num_op, fahrenheit);
|
||||
break;
|
||||
case DeviceValueType::BOOL: {
|
||||
case DeviceValueType::BOOL:
|
||||
Helpers::render_boolean(payload, (bool)*(uint8_t *)(value_p));
|
||||
break;
|
||||
}
|
||||
case DeviceValueType::TIME:
|
||||
Helpers::render_value(payload, *(uint32_t *)(value_p), divider);
|
||||
Helpers::render_value(payload, *(uint32_t *)(value_p), num_op);
|
||||
break;
|
||||
case DeviceValueType::STRING:
|
||||
if (Helpers::hasValue((char *)(value_p))) {
|
||||
@@ -648,7 +713,8 @@ std::string EMSdevice::get_value_uom(const char * key) const {
|
||||
|
||||
// look up key in our device value list
|
||||
for (const auto & dv : devicevalues_) {
|
||||
if ((!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.full_name) && (read_flash_string(dv.full_name) == key_p)) {
|
||||
auto fullname = Helpers::translated_fword(dv.fullname);
|
||||
if ((!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && fullname) && (read_flash_string(fullname) == key_p)) {
|
||||
// ignore TIME since "minutes" is already added to the string value
|
||||
if ((dv.uom == DeviceValueUOM::NONE) || (dv.uom == DeviceValueUOM::MINUTES)) {
|
||||
break;
|
||||
@@ -669,11 +735,13 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
JsonArray data = output.createNestedArray("data");
|
||||
|
||||
for (auto & dv : devicevalues_) {
|
||||
auto fullname = Helpers::translated_fword(dv.fullname);
|
||||
|
||||
// check conditions:
|
||||
// 1. full_name cannot be empty
|
||||
// 1. fullname cannot be empty
|
||||
// 2. it must have a valid value, if it is not a command like 'reset'
|
||||
// 3. show favorites first
|
||||
if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && dv.full_name && (dv.hasValue() || (dv.type == DeviceValueType::CMD))) {
|
||||
if (!dv.has_state(DeviceValueState::DV_WEB_EXCLUDE) && fullname && (dv.hasValue() || (dv.type == DeviceValueType::CMD))) {
|
||||
JsonObject obj = data.createNestedObject(); // create the object, we know there is a value
|
||||
uint8_t fahrenheit = 0;
|
||||
|
||||
@@ -691,33 +759,28 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
|
||||
// handle ENUMs
|
||||
else if ((dv.type == DeviceValueType::ENUM) && (*(uint8_t *)(dv.value_p) < dv.options_size)) {
|
||||
obj["v"] = dv.options[*(uint8_t *)(dv.value_p)];
|
||||
obj["v"] = Helpers::translated_word(dv.options[*(uint8_t *)(dv.value_p)]);
|
||||
}
|
||||
|
||||
// handle numbers
|
||||
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;
|
||||
// note, the nested if's is necessary due to the way the ArduinoJson templates are pre-processed by the compiler
|
||||
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);
|
||||
obj["v"] = Helpers::transformNumFloat(*(int8_t *)(dv.value_p), dv.numeric_operator, 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);
|
||||
obj["v"] = Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), dv.numeric_operator, 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);
|
||||
obj["v"] = Helpers::transformNumFloat(*(int16_t *)(dv.value_p), dv.numeric_operator, 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);
|
||||
obj["v"] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), dv.numeric_operator, 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);
|
||||
obj["v"] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), dv.numeric_operator, 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
|
||||
obj["v"] = dv.numeric_operator ? (*(uint32_t *)(dv.value_p) / dv.numeric_operator) : *(uint32_t *)(dv.value_p);
|
||||
} else {
|
||||
// must have a value for sorting to work
|
||||
obj["v"] = "";
|
||||
obj["v"] = ""; // must have a value for sorting to work
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,9 +791,9 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
|
||||
// add name, prefixing the tag if it exists. This is the id used in the WebUI table and must be unique
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
obj["id"] = mask + read_flash_string(dv.full_name);
|
||||
obj["id"] = mask + Helpers::translated_word(dv.fullname);
|
||||
} else {
|
||||
obj["id"] = mask + tag_to_string(dv.tag) + " " + read_flash_string(dv.full_name);
|
||||
obj["id"] = mask + tag_to_string(dv.tag) + " " + Helpers::translated_word(dv.fullname);
|
||||
}
|
||||
|
||||
// add commands and options
|
||||
@@ -741,12 +804,14 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
} 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]));
|
||||
auto enum_str = Helpers::translated_word(dv.options[i]);
|
||||
if (!enum_str.empty()) {
|
||||
l.add(enum_str);
|
||||
}
|
||||
}
|
||||
} else if (dv.type == DeviceValueType::BOOL) {
|
||||
@@ -758,18 +823,19 @@ void EMSdevice::generate_values_web(JsonObject & output) {
|
||||
// add command help template
|
||||
else if (dv.type == DeviceValueType::STRING || dv.type == DeviceValueType::CMD) {
|
||||
if (dv.options_size == 1) {
|
||||
obj["h"] = dv.options[0];
|
||||
obj["h"] = dv.options_single[0]; // NOT translated
|
||||
}
|
||||
}
|
||||
// add steps to numeric values with divider/multiplier
|
||||
// handle INTs
|
||||
// add steps to numeric values with numeric_operator
|
||||
else {
|
||||
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);
|
||||
char s[10];
|
||||
if (dv.numeric_operator > 0) {
|
||||
obj["s"] = Helpers::render_value(s, (float)1 / dv.numeric_operator, 1);
|
||||
} else if (dv.numeric_operator < 0) {
|
||||
obj["s"] = Helpers::render_value(s, (-1) * dv.numeric_operator, 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);
|
||||
@@ -804,39 +870,45 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
|
||||
// handle ENUMs
|
||||
else if ((dv.type == DeviceValueType::ENUM) && (*(uint8_t *)(dv.value_p) < dv.options_size)) {
|
||||
obj["v"] = dv.options[*(uint8_t *)(dv.value_p)];
|
||||
obj["v"] = Helpers::translated_word(dv.options[*(uint8_t *)(dv.value_p)]);
|
||||
}
|
||||
|
||||
// handle Integers and Floats
|
||||
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
|
||||
uint8_t divider = 0;
|
||||
uint8_t factor = 1;
|
||||
if (dv.options_size == 1) {
|
||||
auto s_str = read_flash_string(dv.options[0]); // prevent object backing the pointer will be destroyed at the end of the full-expression
|
||||
const char * s = s_str.c_str();
|
||||
if (s[0] == '*') {
|
||||
factor = Helpers::atoint(&s[1]);
|
||||
} else {
|
||||
divider = Helpers::atoint(s);
|
||||
}
|
||||
int8_t num_op = dv.numeric_operator;
|
||||
bool make_float;
|
||||
if (num_op == 0) {
|
||||
// no changes to number
|
||||
make_float = false;
|
||||
num_op = 1; // so it gets *1
|
||||
} else if (num_op < 0) {
|
||||
// negative numbers, convert to a positive multiplier
|
||||
make_float = false;
|
||||
num_op *= -1;
|
||||
} else {
|
||||
// has a divider, make it a float
|
||||
make_float = true;
|
||||
}
|
||||
|
||||
// always convert temperatures to floats with 1 decimal place
|
||||
if ((dv.uom == DeviceValueUOM::DEGREES) || (dv.uom == DeviceValueUOM::DEGREES_R)) {
|
||||
make_float = true;
|
||||
}
|
||||
|
||||
if (dv.type == DeviceValueType::INT) {
|
||||
obj["v"] = divider ? Helpers::round2(*(int8_t *)(dv.value_p), divider) : *(int8_t *)(dv.value_p) * factor;
|
||||
obj["v"] = make_float ? Helpers::transformNumFloat(*(int8_t *)(dv.value_p), num_op) : *(int8_t *)(dv.value_p) * num_op;
|
||||
} else if (dv.type == DeviceValueType::UINT) {
|
||||
obj["v"] = divider ? Helpers::round2(*(uint8_t *)(dv.value_p), divider) : *(uint8_t *)(dv.value_p) * factor;
|
||||
obj["v"] = make_float ? Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), num_op) : *(uint8_t *)(dv.value_p) * num_op;
|
||||
} else if (dv.type == DeviceValueType::SHORT) {
|
||||
obj["v"] = divider ? Helpers::round2(*(int16_t *)(dv.value_p), divider) : *(int16_t *)(dv.value_p) * factor;
|
||||
obj["v"] = make_float ? Helpers::transformNumFloat(*(int16_t *)(dv.value_p), num_op) : *(int16_t *)(dv.value_p) * num_op;
|
||||
} else if (dv.type == DeviceValueType::USHORT) {
|
||||
obj["v"] = divider ? Helpers::round2(*(uint16_t *)(dv.value_p), divider) : *(uint16_t *)(dv.value_p) * factor;
|
||||
obj["v"] = make_float ? Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), num_op) : *(uint16_t *)(dv.value_p) * num_op;
|
||||
} else if (dv.type == DeviceValueType::ULONG) {
|
||||
obj["v"] = divider ? Helpers::round2(*(uint32_t *)(dv.value_p), divider) : *(uint32_t *)(dv.value_p) * factor;
|
||||
obj["v"] = make_float ? Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op) : *(uint32_t *)(dv.value_p) * num_op;
|
||||
} else if (dv.type == DeviceValueType::TIME) {
|
||||
uint32_t time_value = *(uint32_t *)(dv.value_p);
|
||||
obj["v"] = (divider > 0) ? time_value / divider : time_value * factor; // sometimes we need to divide by 60
|
||||
// sometimes we need to divide by 60
|
||||
obj["v"] = (num_op == DeviceValueNumOp::DV_NUMOP_DIV60) ? (uint32_t)Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op)
|
||||
: *(uint32_t *)(dv.value_p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -850,13 +922,14 @@ void EMSdevice::generate_values_web_customization(JsonArray & output) {
|
||||
|
||||
// n is the fullname, and can be optional
|
||||
// don't add the fullname if its a command
|
||||
auto translated_full_name = Helpers::translated_fword(dv.fullname);
|
||||
if (dv.type != DeviceValueType::CMD) {
|
||||
if (dv.full_name) {
|
||||
if (translated_full_name) {
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
obj["n"] = dv.full_name;
|
||||
obj["n"] = read_flash_string(translated_full_name);
|
||||
} else {
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(dv.full_name).c_str());
|
||||
snprintf(name, sizeof(name), "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(translated_full_name).c_str());
|
||||
obj["n"] = name;
|
||||
}
|
||||
}
|
||||
@@ -930,7 +1003,8 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
// search device value with this tag
|
||||
for (auto & dv : devicevalues_) {
|
||||
if (strcmp(command_s, Helpers::toLower(read_flash_string(dv.short_name)).c_str()) == 0 && (tag <= 0 || tag == dv.tag)) {
|
||||
int8_t divider = (dv.options_size == 1) ? Helpers::atoint(read_flash_string(dv.options[0]).c_str()) : 0;
|
||||
int8_t num_op = dv.numeric_operator;
|
||||
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0 : (dv.uom == DeviceValueUOM::DEGREES) ? 2 : (dv.uom == DeviceValueUOM::DEGREES_R) ? 1 : 0;
|
||||
|
||||
const char * type = "type";
|
||||
@@ -938,12 +1012,12 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
|
||||
json["name"] = dv.short_name;
|
||||
|
||||
if (dv.full_name != nullptr) {
|
||||
const char * fullname = "fullname";
|
||||
auto fullname = Helpers::translated_fword(dv.fullname);
|
||||
if (fullname != nullptr) {
|
||||
if ((dv.tag == DeviceValueTAG::TAG_NONE) || tag_to_string(dv.tag).empty()) {
|
||||
json[fullname] = dv.full_name;
|
||||
json["fullname"] = fullname;
|
||||
} else {
|
||||
json[fullname] = tag_to_string(dv.tag) + " " + read_flash_string(dv.full_name);
|
||||
json["fullname"] = tag_to_string(dv.tag) + " " + read_flash_string(fullname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -957,48 +1031,48 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
if (EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX) {
|
||||
json[value] = (uint8_t)(*(uint8_t *)(dv.value_p));
|
||||
} else {
|
||||
json[value] = dv.options[*(uint8_t *)(dv.value_p)]; // text
|
||||
json[value] = Helpers::translated_word(dv.options[*(uint8_t *)(dv.value_p)]); // text
|
||||
}
|
||||
}
|
||||
json[type] = F_(enum);
|
||||
JsonArray enum_ = json.createNestedArray(F_(enum));
|
||||
for (uint8_t i = 0; i < dv.options_size; i++) {
|
||||
enum_.add(dv.options[i]);
|
||||
enum_.add(Helpers::translated_word(dv.options[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceValueType::USHORT:
|
||||
if (Helpers::hasValue(*(uint16_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(uint16_t *)(dv.value_p), divider, fahrenheit);
|
||||
json[value] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), num_op, fahrenheit);
|
||||
}
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
case DeviceValueType::UINT:
|
||||
if (Helpers::hasValue(*(uint8_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(uint8_t *)(dv.value_p), divider, fahrenheit);
|
||||
json[value] = Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), num_op, fahrenheit);
|
||||
}
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
case DeviceValueType::SHORT:
|
||||
if (Helpers::hasValue(*(int16_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(int16_t *)(dv.value_p), divider, fahrenheit);
|
||||
json[value] = Helpers::transformNumFloat(*(int16_t *)(dv.value_p), num_op, fahrenheit);
|
||||
}
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
case DeviceValueType::INT:
|
||||
if (Helpers::hasValue(*(int8_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(int8_t *)(dv.value_p), divider, fahrenheit);
|
||||
json[value] = Helpers::transformNumFloat(*(int8_t *)(dv.value_p), num_op, fahrenheit);
|
||||
}
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
|
||||
case DeviceValueType::ULONG:
|
||||
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(uint32_t *)(dv.value_p), divider);
|
||||
json[value] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op);
|
||||
}
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
@@ -1020,7 +1094,7 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
|
||||
case DeviceValueType::TIME:
|
||||
if (Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||
json[value] = Helpers::round2(*(uint32_t *)(dv.value_p), divider);
|
||||
json[value] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op);
|
||||
}
|
||||
json[type] = F_(number);
|
||||
break;
|
||||
@@ -1037,13 +1111,13 @@ bool EMSdevice::get_value_info(JsonObject & output, const char * cmd, const int8
|
||||
if (dv.options_size > 1) {
|
||||
JsonArray enum_ = json.createNestedArray(F_(enum));
|
||||
for (uint8_t i = 0; i < dv.options_size; i++) {
|
||||
enum_.add(dv.options[i]);
|
||||
enum_.add(Helpers::translated_word(dv.options[i]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
json[type] = F_(unknown);
|
||||
json[type] = Helpers::translated_word(FL_(unknown));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1122,12 +1196,14 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
dv.remove_state(DeviceValueState::DV_ACTIVE);
|
||||
}
|
||||
|
||||
auto fullname = Helpers::translated_fword(dv.fullname);
|
||||
|
||||
// check conditions:
|
||||
// 1. it must have a valid value (state is active)
|
||||
// 2. it must have a visible flag
|
||||
// 3. it must match the given tag filter or have an empty tag
|
||||
// 4. it must not have the exclude flag set or outputs to console
|
||||
if (dv.has_state(DeviceValueState::DV_ACTIVE) && dv.full_name && (tag_filter == DeviceValueTAG::TAG_NONE || tag_filter == dv.tag)
|
||||
if (dv.has_state(DeviceValueState::DV_ACTIVE) && fullname && (tag_filter == DeviceValueTAG::TAG_NONE || tag_filter == dv.tag)
|
||||
&& (output_target == OUTPUT_TARGET::CONSOLE || !dv.has_state(DeviceValueState::DV_API_MQTT_EXCLUDE))) {
|
||||
has_values = true; // flagged if we actually have data
|
||||
|
||||
@@ -1136,11 +1212,12 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
|
||||
// create the name for the JSON key
|
||||
char name[80];
|
||||
|
||||
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
|
||||
if (have_tag) {
|
||||
snprintf(name, 80, "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(dv.full_name).c_str()); // prefix the tag
|
||||
snprintf(name, 80, "%s %s", tag_to_string(dv.tag).c_str(), read_flash_string(fullname).c_str()); // prefix the tag
|
||||
} else {
|
||||
strlcpy(name, read_flash_string(dv.full_name).c_str(), sizeof(name)); // use full name
|
||||
strlcpy(name, read_flash_string(fullname).c_str(), sizeof(name)); // use full name
|
||||
}
|
||||
} else {
|
||||
strlcpy(name, read_flash_string(dv.short_name).c_str(), sizeof(name)); // use short name
|
||||
@@ -1184,82 +1261,87 @@ bool EMSdevice::generate_values(JsonObject & output, const uint8_t tag_filter, c
|
||||
if (EMSESP::system_.enum_format() == ENUM_FORMAT_INDEX) {
|
||||
json[name] = (uint8_t)(*(uint8_t *)(dv.value_p));
|
||||
} else {
|
||||
json[name] = dv.options[*(uint8_t *)(dv.value_p)];
|
||||
json[name] = Helpers::translated_word(dv.options[*(uint8_t *)(dv.value_p)]);
|
||||
}
|
||||
}
|
||||
|
||||
// handle Numbers
|
||||
// If a divider is specified, do the division to 2 decimals places and send back as double/float
|
||||
// otherwise force as a whole integer
|
||||
// note: the strange nested if's is necessary due to the way the ArduinoJson templates are pre-processed by the compiler
|
||||
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
|
||||
uint8_t divider = 0;
|
||||
uint8_t factor = 1;
|
||||
if (dv.options_size == 1) {
|
||||
auto s_str = read_flash_string(dv.options[0]); // prevent object backing the pointer will be destroyed at the end of the full-expression
|
||||
const char * s = s_str.c_str();
|
||||
if (s[0] == '*') {
|
||||
factor = Helpers::atoint(&s[1]);
|
||||
} else {
|
||||
divider = Helpers::atoint(s);
|
||||
}
|
||||
}
|
||||
|
||||
// fahrenheit, 0 is no converstion other 1 or 2. not sure why?
|
||||
// fahrenheit, 0 is no conversion other 1 or 2. not sure why?
|
||||
uint8_t fahrenheit = !EMSESP::system_.fahrenheit() ? 0
|
||||
: (dv.uom == DeviceValueUOM::DEGREES) ? 2
|
||||
: (dv.uom == DeviceValueUOM::DEGREES_R) ? 1
|
||||
: 0;
|
||||
|
||||
int8_t num_op = dv.numeric_operator;
|
||||
bool make_float;
|
||||
if (num_op == 0) {
|
||||
// no changes to number
|
||||
make_float = false;
|
||||
num_op = 1; // so it gets *1
|
||||
} else if (num_op < 0) {
|
||||
// negative numbers, convert to a positive multiplier
|
||||
make_float = false;
|
||||
num_op *= -1;
|
||||
} else {
|
||||
// has a divider, make it a float
|
||||
make_float = true;
|
||||
}
|
||||
|
||||
// always convert temperatures to floats with 1 decimal place
|
||||
bool make_float = (divider || (dv.uom == DeviceValueUOM::DEGREES) || (dv.uom == DeviceValueUOM::DEGREES_R));
|
||||
if ((dv.uom == DeviceValueUOM::DEGREES) || (dv.uom == DeviceValueUOM::DEGREES_R)) {
|
||||
make_float = true;
|
||||
}
|
||||
|
||||
if (dv.type == DeviceValueType::INT) {
|
||||
if (make_float) {
|
||||
json[name] = Helpers::round2(*(int8_t *)(dv.value_p), divider, fahrenheit);
|
||||
json[name] = Helpers::transformNumFloat(*(int8_t *)(dv.value_p), num_op, fahrenheit);
|
||||
} else {
|
||||
json[name] = *(int8_t *)(dv.value_p) * factor;
|
||||
json[name] = *(int8_t *)(dv.value_p) * num_op;
|
||||
}
|
||||
} else if (dv.type == DeviceValueType::UINT) {
|
||||
if (make_float) {
|
||||
json[name] = Helpers::round2(*(uint8_t *)(dv.value_p), divider, fahrenheit);
|
||||
json[name] = Helpers::transformNumFloat(*(uint8_t *)(dv.value_p), num_op, fahrenheit);
|
||||
} else {
|
||||
json[name] = *(uint8_t *)(dv.value_p) * factor;
|
||||
json[name] = *(uint8_t *)(dv.value_p) * num_op;
|
||||
}
|
||||
} else if (dv.type == DeviceValueType::SHORT) {
|
||||
if (make_float) {
|
||||
json[name] = Helpers::round2(*(int16_t *)(dv.value_p), divider, fahrenheit);
|
||||
json[name] = Helpers::transformNumFloat(*(int16_t *)(dv.value_p), num_op, fahrenheit);
|
||||
} else {
|
||||
json[name] = *(int16_t *)(dv.value_p) * factor;
|
||||
json[name] = *(int16_t *)(dv.value_p) * num_op;
|
||||
}
|
||||
} else if (dv.type == DeviceValueType::USHORT) {
|
||||
if (make_float) {
|
||||
json[name] = Helpers::round2(*(uint16_t *)(dv.value_p), divider, fahrenheit);
|
||||
json[name] = Helpers::transformNumFloat(*(uint16_t *)(dv.value_p), num_op, fahrenheit);
|
||||
} else {
|
||||
json[name] = *(uint16_t *)(dv.value_p) * factor;
|
||||
json[name] = *(uint16_t *)(dv.value_p) * num_op;
|
||||
}
|
||||
} else if (dv.type == DeviceValueType::ULONG) {
|
||||
if (make_float) {
|
||||
json[name] = Helpers::round2(*(uint32_t *)(dv.value_p), divider, fahrenheit);
|
||||
json[name] = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op, fahrenheit);
|
||||
} else {
|
||||
json[name] = *(uint32_t *)(dv.value_p) * factor;
|
||||
json[name] = *(uint32_t *)(dv.value_p) * num_op;
|
||||
}
|
||||
} else if ((dv.type == DeviceValueType::TIME) && Helpers::hasValue(*(uint32_t *)(dv.value_p))) {
|
||||
uint32_t time_value = *(uint32_t *)(dv.value_p);
|
||||
time_value = Helpers::round2(time_value, divider); // sometimes we need to divide by 60
|
||||
uint32_t time_value;
|
||||
if (num_op == DeviceValueNumOp::DV_NUMOP_DIV60) {
|
||||
// sometimes we need to divide by 60
|
||||
time_value = Helpers::transformNumFloat(*(uint32_t *)(dv.value_p), num_op);
|
||||
} else {
|
||||
time_value = *(uint32_t *)(dv.value_p);
|
||||
}
|
||||
if (output_target == OUTPUT_TARGET::API_VERBOSE || output_target == OUTPUT_TARGET::CONSOLE) {
|
||||
char time_s[40];
|
||||
char time_s[60];
|
||||
snprintf(time_s,
|
||||
sizeof(time_s),
|
||||
"%d %s %d %s %d %s",
|
||||
(time_value / 1440),
|
||||
read_flash_string(F_(days)).c_str(),
|
||||
Helpers::translated_word(FL_(days)).c_str(),
|
||||
((time_value % 1440) / 60),
|
||||
read_flash_string(F_(hours)).c_str(),
|
||||
Helpers::translated_word(FL_(hours)).c_str(),
|
||||
(time_value % 60),
|
||||
read_flash_string(F_(minutes)).c_str());
|
||||
Helpers::translated_word(FL_(minutes)).c_str());
|
||||
json[name] = time_s;
|
||||
} else {
|
||||
json[name] = time_value;
|
||||
|
||||
Reference in New Issue
Block a user