diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp index e3b9119108..c43cd07fe0 100644 --- a/esphome/components/graph/graph.cpp +++ b/esphome/components/graph/graph.cpp @@ -232,17 +232,19 @@ void GraphLegend::init(Graph *g) { ESP_LOGI(TAGL, " %s %d %d", txtstr.c_str(), fw, fh); if (this->values_ != VALUE_POSITION_TYPE_NONE) { - std::string valstr = - value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); + char valstr[VALUE_ACCURACY_MAX_LEN]; if (this->units_) { - valstr += trace->sensor_->get_unit_of_measurement_ref(); + value_accuracy_with_uom_to_buf(valstr, trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals(), + trace->sensor_->get_unit_of_measurement_ref()); + } else { + value_accuracy_to_buf(valstr, trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); } - this->font_value_->measure(valstr.c_str(), &fw, &fos, &fbl, &fh); + this->font_value_->measure(valstr, &fw, &fos, &fbl, &fh); if (fw > valw) valw = fw; if (fh > valh) valh = fh; - ESP_LOGI(TAGL, " %s %d %d", valstr.c_str(), fw, fh); + ESP_LOGI(TAGL, " %s %d %d", valstr, fw, fh); } } // Add extra margin @@ -368,13 +370,15 @@ void Graph::draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_of if (legend_->values_ != VALUE_POSITION_TYPE_NONE) { int xv = x + legend_->xv_; int yv = y + legend_->yv_; - std::string valstr = - value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); + char valstr[VALUE_ACCURACY_MAX_LEN]; if (legend_->units_) { - valstr += trace->sensor_->get_unit_of_measurement_ref(); + value_accuracy_with_uom_to_buf(valstr, trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals(), + trace->sensor_->get_unit_of_measurement_ref()); + } else { + value_accuracy_to_buf(valstr, trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); } - buff->printf(xv, yv, legend_->font_value_, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", valstr.c_str()); - ESP_LOGV(TAG, " value: %s", valstr.c_str()); + buff->printf(xv, yv, legend_->font_value_, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", valstr); + ESP_LOGV(TAG, " value: %s", valstr); } x += legend_->xs_; y += legend_->ys_; diff --git a/esphome/components/mqtt/custom_mqtt_device.cpp b/esphome/components/mqtt/custom_mqtt_device.cpp index 25a8a82066..c900e3861d 100644 --- a/esphome/components/mqtt/custom_mqtt_device.cpp +++ b/esphome/components/mqtt/custom_mqtt_device.cpp @@ -12,8 +12,9 @@ bool CustomMQTTDevice::publish(const std::string &topic, const std::string &payl return global_mqtt_client->publish(topic, payload, qos, retain); } bool CustomMQTTDevice::publish(const std::string &topic, float value, int8_t number_decimals) { - auto str = value_accuracy_to_string(value, number_decimals); - return this->publish(topic, str); + char buf[VALUE_ACCURACY_MAX_LEN]; + value_accuracy_to_buf(buf, value, number_decimals); + return this->publish(topic, buf); } bool CustomMQTTDevice::publish(const std::string &topic, int value) { char buffer[24]; diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index 625fb715a7..c7e086115b 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -291,35 +291,36 @@ bool MQTTClimateComponent::publish_state_() { success = false; int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals(); + char payload[VALUE_ACCURACY_MAX_LEN]; if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE) && !std::isnan(this->device_->current_temperature)) { - std::string payload = value_accuracy_to_string(this->device_->current_temperature, current_accuracy); + value_accuracy_to_buf(payload, this->device_->current_temperature, current_accuracy); if (!this->publish(this->get_current_temperature_state_topic(), payload)) success = false; } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { - std::string payload = value_accuracy_to_string(this->device_->target_temperature_low, target_accuracy); + value_accuracy_to_buf(payload, this->device_->target_temperature_low, target_accuracy); if (!this->publish(this->get_target_temperature_low_state_topic(), payload)) success = false; - payload = value_accuracy_to_string(this->device_->target_temperature_high, target_accuracy); + value_accuracy_to_buf(payload, this->device_->target_temperature_high, target_accuracy); if (!this->publish(this->get_target_temperature_high_state_topic(), payload)) success = false; } else { - std::string payload = value_accuracy_to_string(this->device_->target_temperature, target_accuracy); + value_accuracy_to_buf(payload, this->device_->target_temperature, target_accuracy); if (!this->publish(this->get_target_temperature_state_topic(), payload)) success = false; } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY) && !std::isnan(this->device_->current_humidity)) { - std::string payload = value_accuracy_to_string(this->device_->current_humidity, 0); + value_accuracy_to_buf(payload, this->device_->current_humidity, 0); if (!this->publish(this->get_current_humidity_state_topic(), payload)) success = false; } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY) && !std::isnan(this->device_->target_humidity)) { - std::string payload = value_accuracy_to_string(this->device_->target_humidity, 0); + value_accuracy_to_buf(payload, this->device_->target_humidity, 0); if (!this->publish(this->get_target_humidity_state_topic(), payload)) success = false; } diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 4505027485..2164b5ca44 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -98,12 +98,14 @@ bool MQTTCoverComponent::publish_state() { auto traits = this->cover_->get_traits(); bool success = true; if (traits.get_supports_position()) { - std::string pos = value_accuracy_to_string(roundf(this->cover_->position * 100), 0); + char pos[VALUE_ACCURACY_MAX_LEN]; + value_accuracy_to_buf(pos, roundf(this->cover_->position * 100), 0); if (!this->publish(this->get_position_state_topic(), pos)) success = false; } if (traits.get_supports_tilt()) { - std::string pos = value_accuracy_to_string(roundf(this->cover_->tilt * 100), 0); + char pos[VALUE_ACCURACY_MAX_LEN]; + value_accuracy_to_buf(pos, roundf(this->cover_->tilt * 100), 0); if (!this->publish(this->get_tilt_state_topic(), pos)) success = false; } diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index 14eb160e72..cfe6923a5f 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -82,7 +82,9 @@ bool MQTTSensorComponent::publish_state(float value) { if (mqtt::global_mqtt_client->is_publish_nan_as_none() && std::isnan(value)) return this->publish(this->get_state_topic_(), "None"); int8_t accuracy = this->sensor_->get_accuracy_decimals(); - return this->publish(this->get_state_topic_(), value_accuracy_to_string(value, accuracy)); + char buf[VALUE_ACCURACY_MAX_LEN]; + value_accuracy_to_buf(buf, value, accuracy); + return this->publish(this->get_state_topic_(), buf); } } // namespace esphome::mqtt diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index a4c893f84b..b4cc367bc5 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -73,7 +73,8 @@ bool MQTTValveComponent::publish_state() { auto traits = this->valve_->get_traits(); bool success = true; if (traits.get_supports_position()) { - std::string pos = value_accuracy_to_string(roundf(this->valve_->position * 100), 0); + char pos[VALUE_ACCURACY_MAX_LEN]; + value_accuracy_to_buf(pos, roundf(this->valve_->position * 100), 0); if (!this->publish(this->get_position_state_topic(), pos)) success = false; } diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index dd577a4dbc..e2639a2298 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -194,7 +194,9 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor stream->print(ESPHOME_F("\",unit=\"")); stream->print(obj->get_unit_of_measurement_ref().c_str()); stream->print(ESPHOME_F("\"} ")); - stream->print(value_accuracy_to_string(obj->state, obj->get_accuracy_decimals()).c_str()); + char value_buf[VALUE_ACCURACY_MAX_LEN]; + value_accuracy_to_buf(value_buf, obj->state, obj->get_accuracy_decimals()); + stream->print(value_buf); stream->print(ESPHOME_F("\n")); } else { // Invalid state @@ -954,7 +956,7 @@ void PrometheusHandler::climate_setting_row_(AsyncResponseStream *stream, climat void PrometheusHandler::climate_value_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area, std::string &node, std::string &friendly_name, std::string &category, - std::string &climate_value) { + const char *climate_value) { stream->print(ESPHOME_F("esphome_climate_value{id=\"")); stream->print(relabel_id_(obj).c_str()); add_area_label_(stream, area); @@ -965,7 +967,7 @@ void PrometheusHandler::climate_value_row_(AsyncResponseStream *stream, climate: stream->print(ESPHOME_F("\",category=\"")); stream->print(category.c_str()); stream->print(ESPHOME_F("\"} ")); - stream->print(climate_value.c_str()); + stream->print(climate_value); stream->print(ESPHOME_F("\n")); } @@ -1003,14 +1005,15 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima // Now see if traits is supported int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals(); + char value_buf[VALUE_ACCURACY_MAX_LEN]; // max temp std::string max_temp = "maximum_temperature"; - auto max_temp_value = value_accuracy_to_string(traits.get_visual_max_temperature(), target_accuracy); - climate_value_row_(stream, obj, area, node, friendly_name, max_temp, max_temp_value); - // max temp - std::string min_temp = "mininum_temperature"; - auto min_temp_value = value_accuracy_to_string(traits.get_visual_min_temperature(), target_accuracy); - climate_value_row_(stream, obj, area, node, friendly_name, min_temp, min_temp_value); + value_accuracy_to_buf(value_buf, traits.get_visual_max_temperature(), target_accuracy); + climate_value_row_(stream, obj, area, node, friendly_name, max_temp, value_buf); + // min temp + std::string min_temp = "minimum_temperature"; + value_accuracy_to_buf(value_buf, traits.get_visual_min_temperature(), target_accuracy); + climate_value_row_(stream, obj, area, node, friendly_name, min_temp, value_buf); // now check optional traits if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { std::string current_temp = "current_temperature"; @@ -1018,8 +1021,8 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima climate_failed_row_(stream, obj, area, node, friendly_name, current_temp, true); any_failures = true; } else { - auto current_temp_value = value_accuracy_to_string(obj->current_temperature, current_accuracy); - climate_value_row_(stream, obj, area, node, friendly_name, current_temp, current_temp_value); + value_accuracy_to_buf(value_buf, obj->current_temperature, current_accuracy); + climate_value_row_(stream, obj, area, node, friendly_name, current_temp, value_buf); climate_failed_row_(stream, obj, area, node, friendly_name, current_temp, false); } } @@ -1029,8 +1032,8 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima climate_failed_row_(stream, obj, area, node, friendly_name, current_humidity, true); any_failures = true; } else { - auto current_humidity_value = value_accuracy_to_string(obj->current_humidity, 0); - climate_value_row_(stream, obj, area, node, friendly_name, current_humidity, current_humidity_value); + value_accuracy_to_buf(value_buf, obj->current_humidity, 0); + climate_value_row_(stream, obj, area, node, friendly_name, current_humidity, value_buf); climate_failed_row_(stream, obj, area, node, friendly_name, current_humidity, false); } } @@ -1040,23 +1043,23 @@ void PrometheusHandler::climate_row_(AsyncResponseStream *stream, climate::Clima climate_failed_row_(stream, obj, area, node, friendly_name, target_humidity, true); any_failures = true; } else { - auto target_humidity_value = value_accuracy_to_string(obj->target_humidity, 0); - climate_value_row_(stream, obj, area, node, friendly_name, target_humidity, target_humidity_value); + value_accuracy_to_buf(value_buf, obj->target_humidity, 0); + climate_value_row_(stream, obj, area, node, friendly_name, target_humidity, value_buf); climate_failed_row_(stream, obj, area, node, friendly_name, target_humidity, false); } } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { std::string target_temp_low = "target_temperature_low"; - auto target_temp_low_value = value_accuracy_to_string(obj->target_temperature_low, target_accuracy); - climate_value_row_(stream, obj, area, node, friendly_name, target_temp_low, target_temp_low_value); + value_accuracy_to_buf(value_buf, obj->target_temperature_low, target_accuracy); + climate_value_row_(stream, obj, area, node, friendly_name, target_temp_low, value_buf); std::string target_temp_high = "target_temperature_high"; - auto target_temp_high_value = value_accuracy_to_string(obj->target_temperature_high, target_accuracy); - climate_value_row_(stream, obj, area, node, friendly_name, target_temp_high, target_temp_high_value); + value_accuracy_to_buf(value_buf, obj->target_temperature_high, target_accuracy); + climate_value_row_(stream, obj, area, node, friendly_name, target_temp_high, value_buf); } else { std::string target_temp = "target_temperature"; - auto target_temp_value = value_accuracy_to_string(obj->target_temperature, target_accuracy); - climate_value_row_(stream, obj, area, node, friendly_name, target_temp, target_temp_value); + value_accuracy_to_buf(value_buf, obj->target_temperature, target_accuracy); + climate_value_row_(stream, obj, area, node, friendly_name, target_temp, value_buf); } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { std::string climate_trait_category = "action"; diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 24243c8c98..fc48ad67e3 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -207,7 +207,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { void climate_setting_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area, std::string &node, std::string &friendly_name, std::string &setting, const LogString *setting_value); void climate_value_row_(AsyncResponseStream *stream, climate::Climate *obj, std::string &area, std::string &node, - std::string &friendly_name, std::string &category, std::string &climate_value); + std::string &friendly_name, std::string &category, const char *climate_value); #endif web_server_base::WebServerBase *base_; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index bf559d2bc6..396a58464f 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1027,7 +1027,8 @@ enum ParseOnOffState : uint8_t { /// Parse a string that contains either on, off or toggle. ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const char *off = nullptr); -/// Create a string from a value and an accuracy in decimals. +/// @deprecated Allocates heap memory. Use value_accuracy_to_buf() instead. Removed in 2026.7.0. +ESPDEPRECATED("Allocates heap memory. Use value_accuracy_to_buf() instead. Removed in 2026.7.0.", "2026.1.0") std::string value_accuracy_to_string(float value, int8_t accuracy_decimals); /// Maximum buffer size for value_accuracy formatting (float ~15 chars + space + UOM ~40 chars + null)