mirror of
https://github.com/esphome/esphome.git
synced 2025-10-07 12:23:47 +01:00
[web_server] Reduce flash and RAM usage by optimizing string construction
This commit is contained in:
@@ -381,11 +381,14 @@ void WebServer::handle_js_request(AsyncWebServerRequest *request) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Helper functions to reduce code size by avoiding macro expansion
|
// Helper functions to reduce code size by avoiding macro expansion
|
||||||
static void set_json_id(JsonObject &root, EntityBase *obj, const std::string &id, JsonDetail start_config) {
|
static void set_json_id(JsonObject &root, EntityBase *obj, const char *prefix, JsonDetail start_config) {
|
||||||
root["id"] = id;
|
char id_buf[160]; // object_id can be up to 128 chars + prefix + dash + null
|
||||||
|
const auto &object_id = obj->get_object_id();
|
||||||
|
snprintf(id_buf, sizeof(id_buf), "%s-%s", prefix, object_id.c_str());
|
||||||
|
root["id"] = id_buf;
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
root["name"] = obj->get_name();
|
root["name"] = obj->get_name();
|
||||||
root["icon"] = obj->get_icon();
|
root["icon"] = obj->get_icon_ref();
|
||||||
root["entity_category"] = obj->get_entity_category();
|
root["entity_category"] = obj->get_entity_category();
|
||||||
bool is_disabled = obj->is_disabled_by_default();
|
bool is_disabled = obj->is_disabled_by_default();
|
||||||
if (is_disabled)
|
if (is_disabled)
|
||||||
@@ -393,17 +396,19 @@ static void set_json_id(JsonObject &root, EntityBase *obj, const std::string &id
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep as separate function even though only used once: reduces code size by ~48 bytes
|
||||||
|
// by allowing compiler to share code between template instantiations (bool, float, etc.)
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void set_json_value(JsonObject &root, EntityBase *obj, const std::string &id, const T &value,
|
static void set_json_value(JsonObject &root, EntityBase *obj, const char *prefix, const T &value,
|
||||||
JsonDetail start_config) {
|
JsonDetail start_config) {
|
||||||
set_json_id(root, obj, id, start_config);
|
set_json_id(root, obj, prefix, start_config);
|
||||||
root["value"] = value;
|
root["value"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void set_json_icon_state_value(JsonObject &root, EntityBase *obj, const std::string &id,
|
static void set_json_icon_state_value(JsonObject &root, EntityBase *obj, const char *prefix, const std::string &state,
|
||||||
const std::string &state, const T &value, JsonDetail start_config) {
|
const T &value, JsonDetail start_config) {
|
||||||
set_json_value(root, obj, id, value, start_config);
|
set_json_value(root, obj, prefix, value, start_config);
|
||||||
root["state"] = state;
|
root["state"] = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,20 +447,20 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
|
const auto uom_ref = obj->get_unit_of_measurement_ref();
|
||||||
|
|
||||||
// Build JSON directly inline
|
// Build JSON directly inline
|
||||||
std::string state;
|
std::string state;
|
||||||
if (std::isnan(value)) {
|
if (std::isnan(value)) {
|
||||||
state = "NA";
|
state = "NA";
|
||||||
} else {
|
} else {
|
||||||
state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
|
state = value_accuracy_with_uom_to_string(value, obj->get_accuracy_decimals(), uom_ref);
|
||||||
if (!obj->get_unit_of_measurement().empty())
|
|
||||||
state += " " + obj->get_unit_of_measurement();
|
|
||||||
}
|
}
|
||||||
set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
|
set_json_icon_state_value(root, obj, "sensor", state, value, start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
if (!obj->get_unit_of_measurement().empty())
|
if (!uom_ref.empty())
|
||||||
root["uom"] = obj->get_unit_of_measurement();
|
root["uom"] = uom_ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.serialize();
|
return builder.serialize();
|
||||||
@@ -494,7 +499,7 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std:
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_icon_state_value(root, obj, "text_sensor-" + obj->get_object_id(), value, value, start_config);
|
set_json_icon_state_value(root, obj, "text_sensor", value, value, start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
}
|
}
|
||||||
@@ -567,7 +572,7 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_icon_state_value(root, obj, "switch-" + obj->get_object_id(), value ? "ON" : "OFF", value, start_config);
|
set_json_icon_state_value(root, obj, "switch", value ? "ON" : "OFF", value, start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
root["assumed_state"] = obj->assumed_state();
|
root["assumed_state"] = obj->assumed_state();
|
||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
@@ -607,7 +612,7 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config)
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_id(root, obj, "button-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "button", start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
}
|
}
|
||||||
@@ -647,8 +652,7 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_icon_state_value(root, obj, "binary_sensor-" + obj->get_object_id(), value ? "ON" : "OFF", value,
|
set_json_icon_state_value(root, obj, "binary_sensor", value ? "ON" : "OFF", value, start_config);
|
||||||
start_config);
|
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
}
|
}
|
||||||
@@ -717,8 +721,7 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) {
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_icon_state_value(root, obj, "fan-" + obj->get_object_id(), obj->state ? "ON" : "OFF", obj->state,
|
set_json_icon_state_value(root, obj, "fan", obj->state ? "ON" : "OFF", obj->state, start_config);
|
||||||
start_config);
|
|
||||||
const auto traits = obj->get_traits();
|
const auto traits = obj->get_traits();
|
||||||
if (traits.supports_speed()) {
|
if (traits.supports_speed()) {
|
||||||
root["speed_level"] = obj->speed;
|
root["speed_level"] = obj->speed;
|
||||||
@@ -793,7 +796,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_id(root, obj, "light-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "light", start_config);
|
||||||
root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
|
root["state"] = obj->remote_values.is_on() ? "ON" : "OFF";
|
||||||
|
|
||||||
light::LightJSONSchema::dump_json(*obj, root);
|
light::LightJSONSchema::dump_json(*obj, root);
|
||||||
@@ -881,8 +884,8 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) {
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_icon_state_value(root, obj, "cover-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
|
set_json_icon_state_value(root, obj, "cover", obj->is_fully_closed() ? "CLOSED" : "OPEN", obj->position,
|
||||||
obj->position, start_config);
|
start_config);
|
||||||
root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
|
root["current_operation"] = cover::cover_operation_to_str(obj->current_operation);
|
||||||
|
|
||||||
if (obj->get_traits().get_supports_position())
|
if (obj->get_traits().get_supports_position())
|
||||||
@@ -939,7 +942,9 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_id(root, obj, "number-" + obj->get_object_id(), start_config);
|
const auto uom_ref = obj->traits.get_unit_of_measurement_ref();
|
||||||
|
|
||||||
|
set_json_id(root, obj, "number", start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
root["min_value"] =
|
root["min_value"] =
|
||||||
value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step()));
|
value_accuracy_to_string(obj->traits.get_min_value(), step_to_accuracy_decimals(obj->traits.get_step()));
|
||||||
@@ -947,8 +952,8 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
|
|||||||
value_accuracy_to_string(obj->traits.get_max_value(), step_to_accuracy_decimals(obj->traits.get_step()));
|
value_accuracy_to_string(obj->traits.get_max_value(), step_to_accuracy_decimals(obj->traits.get_step()));
|
||||||
root["step"] = value_accuracy_to_string(obj->traits.get_step(), step_to_accuracy_decimals(obj->traits.get_step()));
|
root["step"] = value_accuracy_to_string(obj->traits.get_step(), step_to_accuracy_decimals(obj->traits.get_step()));
|
||||||
root["mode"] = (int) obj->traits.get_mode();
|
root["mode"] = (int) obj->traits.get_mode();
|
||||||
if (!obj->traits.get_unit_of_measurement().empty())
|
if (!uom_ref.empty())
|
||||||
root["uom"] = obj->traits.get_unit_of_measurement();
|
root["uom"] = uom_ref;
|
||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
}
|
}
|
||||||
if (std::isnan(value)) {
|
if (std::isnan(value)) {
|
||||||
@@ -956,10 +961,8 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
|
|||||||
root["state"] = "NA";
|
root["state"] = "NA";
|
||||||
} else {
|
} else {
|
||||||
root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
|
root["value"] = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
|
||||||
std::string state = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
|
root["state"] =
|
||||||
if (!obj->traits.get_unit_of_measurement().empty())
|
value_accuracy_with_uom_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()), uom_ref);
|
||||||
state += " " + obj->traits.get_unit_of_measurement();
|
|
||||||
root["state"] = state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.serialize();
|
return builder.serialize();
|
||||||
@@ -1013,7 +1016,7 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_id(root, obj, "date-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "date", start_config);
|
||||||
std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
|
std::string value = str_sprintf("%d-%02d-%02d", obj->year, obj->month, obj->day);
|
||||||
root["value"] = value;
|
root["value"] = value;
|
||||||
root["state"] = value;
|
root["state"] = value;
|
||||||
@@ -1071,7 +1074,7 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_id(root, obj, "time-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "time", start_config);
|
||||||
std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
|
std::string value = str_sprintf("%02d:%02d:%02d", obj->hour, obj->minute, obj->second);
|
||||||
root["value"] = value;
|
root["value"] = value;
|
||||||
root["state"] = value;
|
root["state"] = value;
|
||||||
@@ -1129,7 +1132,7 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_id(root, obj, "datetime-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "datetime", start_config);
|
||||||
std::string value =
|
std::string value =
|
||||||
str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour, obj->minute, obj->second);
|
str_sprintf("%d-%02d-%02d %02d:%02d:%02d", obj->year, obj->month, obj->day, obj->hour, obj->minute, obj->second);
|
||||||
root["value"] = value;
|
root["value"] = value;
|
||||||
@@ -1184,7 +1187,7 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_id(root, obj, "text-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "text", start_config);
|
||||||
root["min_length"] = obj->traits.get_min_length();
|
root["min_length"] = obj->traits.get_min_length();
|
||||||
root["max_length"] = obj->traits.get_max_length();
|
root["max_length"] = obj->traits.get_max_length();
|
||||||
root["pattern"] = obj->traits.get_pattern();
|
root["pattern"] = obj->traits.get_pattern();
|
||||||
@@ -1245,7 +1248,7 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
|
set_json_icon_state_value(root, obj, "select", value, value, start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
JsonArray opt = root["option"].to<JsonArray>();
|
JsonArray opt = root["option"].to<JsonArray>();
|
||||||
for (auto &option : obj->traits.get_options()) {
|
for (auto &option : obj->traits.get_options()) {
|
||||||
@@ -1314,7 +1317,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
|||||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "climate", start_config);
|
||||||
const auto traits = obj->get_traits();
|
const auto traits = obj->get_traits();
|
||||||
int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
|
int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
|
||||||
int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
|
int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
|
||||||
@@ -1467,8 +1470,7 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_icon_state_value(root, obj, "lock-" + obj->get_object_id(), lock::lock_state_to_string(value), value,
|
set_json_icon_state_value(root, obj, "lock", lock::lock_state_to_string(value), value, start_config);
|
||||||
start_config);
|
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
}
|
}
|
||||||
@@ -1546,8 +1548,8 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) {
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_icon_state_value(root, obj, "valve-" + obj->get_object_id(), obj->is_fully_closed() ? "CLOSED" : "OPEN",
|
set_json_icon_state_value(root, obj, "valve", obj->is_fully_closed() ? "CLOSED" : "OPEN", obj->position,
|
||||||
obj->position, start_config);
|
start_config);
|
||||||
root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
|
root["current_operation"] = valve::valve_operation_to_str(obj->current_operation);
|
||||||
|
|
||||||
if (obj->get_traits().get_supports_position())
|
if (obj->get_traits().get_supports_position())
|
||||||
@@ -1630,8 +1632,8 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro
|
|||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
char buf[16];
|
char buf[16];
|
||||||
set_json_icon_state_value(root, obj, "alarm-control-panel-" + obj->get_object_id(),
|
set_json_icon_state_value(root, obj, "alarm-control-panel", PSTR_LOCAL(alarm_control_panel_state_to_string(value)),
|
||||||
PSTR_LOCAL(alarm_control_panel_state_to_string(value)), value, start_config);
|
value, start_config);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
}
|
}
|
||||||
@@ -1676,7 +1678,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_id(root, obj, "event-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "event", start_config);
|
||||||
if (!event_type.empty()) {
|
if (!event_type.empty()) {
|
||||||
root["event_type"] = event_type;
|
root["event_type"] = event_type;
|
||||||
}
|
}
|
||||||
@@ -1685,7 +1687,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty
|
|||||||
for (auto const &event_type : obj->get_event_types()) {
|
for (auto const &event_type : obj->get_event_types()) {
|
||||||
event_types.add(event_type);
|
event_types.add(event_type);
|
||||||
}
|
}
|
||||||
root["device_class"] = obj->get_device_class();
|
root["device_class"] = obj->get_device_class_ref();
|
||||||
this->add_sorting_info_(root, obj);
|
this->add_sorting_info_(root, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1748,7 +1750,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c
|
|||||||
json::JsonBuilder builder;
|
json::JsonBuilder builder;
|
||||||
JsonObject root = builder.root();
|
JsonObject root = builder.root();
|
||||||
|
|
||||||
set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
|
set_json_id(root, obj, "update", start_config);
|
||||||
root["value"] = obj->update_info.latest_version;
|
root["value"] = obj->update_info.latest_version;
|
||||||
root["state"] = update_state_to_string(obj->state);
|
root["state"] = update_state_to_string(obj->state);
|
||||||
if (start_config == DETAIL_ALL) {
|
if (start_config == DETAIL_ALL) {
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/string_ref.h"
|
||||||
|
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -348,17 +349,32 @@ ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) {
|
|||||||
return PARSE_NONE;
|
return PARSE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) {
|
static inline void normalize_accuracy_decimals(float &value, int8_t &accuracy_decimals) {
|
||||||
if (accuracy_decimals < 0) {
|
if (accuracy_decimals < 0) {
|
||||||
auto multiplier = powf(10.0f, accuracy_decimals);
|
auto multiplier = powf(10.0f, accuracy_decimals);
|
||||||
value = roundf(value * multiplier) / multiplier;
|
value = roundf(value * multiplier) / multiplier;
|
||||||
accuracy_decimals = 0;
|
accuracy_decimals = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) {
|
||||||
|
normalize_accuracy_decimals(value, accuracy_decimals);
|
||||||
char tmp[32]; // should be enough, but we should maybe improve this at some point.
|
char tmp[32]; // should be enough, but we should maybe improve this at some point.
|
||||||
snprintf(tmp, sizeof(tmp), "%.*f", accuracy_decimals, value);
|
snprintf(tmp, sizeof(tmp), "%.*f", accuracy_decimals, value);
|
||||||
return std::string(tmp);
|
return std::string(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string value_accuracy_with_uom_to_string(float value, int8_t accuracy_decimals, StringRef unit_of_measurement) {
|
||||||
|
normalize_accuracy_decimals(value, accuracy_decimals);
|
||||||
|
char tmp[64]; // Increased to accommodate unit of measurement
|
||||||
|
if (unit_of_measurement.empty()) {
|
||||||
|
snprintf(tmp, sizeof(tmp), "%.*f", accuracy_decimals, value);
|
||||||
|
} else {
|
||||||
|
snprintf(tmp, sizeof(tmp), "%.*f %s", accuracy_decimals, value, unit_of_measurement.c_str());
|
||||||
|
}
|
||||||
|
return std::string(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
int8_t step_to_accuracy_decimals(float step) {
|
int8_t step_to_accuracy_decimals(float step) {
|
||||||
// use printf %g to find number of digits based on temperature step
|
// use printf %g to find number of digits based on temperature step
|
||||||
char buf[32];
|
char buf[32];
|
||||||
|
@@ -14,6 +14,10 @@
|
|||||||
|
|
||||||
#include "esphome/core/optional.h"
|
#include "esphome/core/optional.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
class StringRef;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#include <Esp.h>
|
#include <Esp.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -600,6 +604,8 @@ ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const ch
|
|||||||
|
|
||||||
/// Create a string from a value and an accuracy in decimals.
|
/// Create a string from a value and an accuracy in decimals.
|
||||||
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals);
|
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals);
|
||||||
|
/// Create a string from a value, an accuracy in decimals, and a unit of measurement.
|
||||||
|
std::string value_accuracy_with_uom_to_string(float value, int8_t accuracy_decimals, StringRef unit_of_measurement);
|
||||||
|
|
||||||
/// Derive accuracy in decimals from an increment step.
|
/// Derive accuracy in decimals from an increment step.
|
||||||
int8_t step_to_accuracy_decimals(float step);
|
int8_t step_to_accuracy_decimals(float step);
|
||||||
|
Reference in New Issue
Block a user