From 3d40979c966ee6fb866d54e52bff6fa3314218c8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 13 Jan 2026 08:05:04 -1000 Subject: [PATCH] [mqtt] Avoid intermediate string allocations in publish calls (#13174) --- .../components/mqtt/custom_mqtt_device.cpp | 8 +++--- esphome/components/mqtt/mqtt_climate.cpp | 25 ++++++++++--------- esphome/components/mqtt/mqtt_component.cpp | 6 ++++- esphome/components/mqtt/mqtt_component.h | 8 ++++++ esphome/components/mqtt/mqtt_cover.cpp | 8 +++--- esphome/components/mqtt/mqtt_fan.cpp | 5 ++-- esphome/components/mqtt/mqtt_sensor.cpp | 6 ++--- esphome/components/mqtt/mqtt_valve.cpp | 4 +-- 8 files changed, 42 insertions(+), 28 deletions(-) diff --git a/esphome/components/mqtt/custom_mqtt_device.cpp b/esphome/components/mqtt/custom_mqtt_device.cpp index c900e3861d..7ff65bb42c 100644 --- a/esphome/components/mqtt/custom_mqtt_device.cpp +++ b/esphome/components/mqtt/custom_mqtt_device.cpp @@ -13,13 +13,13 @@ bool CustomMQTTDevice::publish(const std::string &topic, const std::string &payl } bool CustomMQTTDevice::publish(const std::string &topic, float value, int8_t number_decimals) { char buf[VALUE_ACCURACY_MAX_LEN]; - value_accuracy_to_buf(buf, value, number_decimals); - return this->publish(topic, buf); + size_t len = value_accuracy_to_buf(buf, value, number_decimals); + return global_mqtt_client->publish(topic, buf, len); } bool CustomMQTTDevice::publish(const std::string &topic, int value) { char buffer[24]; - sprintf(buffer, "%d", value); - return this->publish(topic, buffer); + int len = snprintf(buffer, sizeof(buffer), "%d", value); + return global_mqtt_client->publish(topic, buffer, len); } bool CustomMQTTDevice::publish_json(const std::string &topic, const json::json_build_t &f, uint8_t qos, bool retain) { return global_mqtt_client->publish_json(topic, f, qos, retain); diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index c7e086115b..37d643f9e7 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -292,36 +292,37 @@ bool MQTTClimateComponent::publish_state_() { 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]; + size_t len; if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE) && !std::isnan(this->device_->current_temperature)) { - value_accuracy_to_buf(payload, this->device_->current_temperature, current_accuracy); - if (!this->publish(this->get_current_temperature_state_topic(), payload)) + len = value_accuracy_to_buf(payload, this->device_->current_temperature, current_accuracy); + if (!this->publish(this->get_current_temperature_state_topic(), payload, len)) success = false; } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { - value_accuracy_to_buf(payload, this->device_->target_temperature_low, target_accuracy); - if (!this->publish(this->get_target_temperature_low_state_topic(), payload)) + len = value_accuracy_to_buf(payload, this->device_->target_temperature_low, target_accuracy); + if (!this->publish(this->get_target_temperature_low_state_topic(), payload, len)) success = false; - value_accuracy_to_buf(payload, this->device_->target_temperature_high, target_accuracy); - if (!this->publish(this->get_target_temperature_high_state_topic(), payload)) + len = value_accuracy_to_buf(payload, this->device_->target_temperature_high, target_accuracy); + if (!this->publish(this->get_target_temperature_high_state_topic(), payload, len)) success = false; } else { - value_accuracy_to_buf(payload, this->device_->target_temperature, target_accuracy); - if (!this->publish(this->get_target_temperature_state_topic(), payload)) + len = value_accuracy_to_buf(payload, this->device_->target_temperature, target_accuracy); + if (!this->publish(this->get_target_temperature_state_topic(), payload, len)) success = false; } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY) && !std::isnan(this->device_->current_humidity)) { - value_accuracy_to_buf(payload, this->device_->current_humidity, 0); - if (!this->publish(this->get_current_humidity_state_topic(), payload)) + len = value_accuracy_to_buf(payload, this->device_->current_humidity, 0); + if (!this->publish(this->get_current_humidity_state_topic(), payload, len)) success = false; } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY) && !std::isnan(this->device_->target_humidity)) { - value_accuracy_to_buf(payload, this->device_->target_humidity, 0); - if (!this->publish(this->get_target_humidity_state_topic(), payload)) + len = value_accuracy_to_buf(payload, this->device_->target_humidity, 0); + if (!this->publish(this->get_target_humidity_state_topic(), payload, len)) success = false; } diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 13f496c6a7..20c111de43 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -106,9 +106,13 @@ std::string MQTTComponent::get_command_topic_() const { } bool MQTTComponent::publish(const std::string &topic, const std::string &payload) { + return this->publish(topic, payload.data(), payload.size()); +} + +bool MQTTComponent::publish(const std::string &topic, const char *payload, size_t payload_length) { if (topic.empty()) return false; - return global_mqtt_client->publish(topic, payload, this->qos_, this->retain_); + return global_mqtt_client->publish(topic, payload, payload_length, this->qos_, this->retain_); } bool MQTTComponent::publish_json(const std::string &topic, const json::json_build_t &f) { diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 11dfd093f3..676e3ad35d 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -140,6 +140,14 @@ class MQTTComponent : public Component { */ bool publish(const std::string &topic, const std::string &payload); + /** Send a MQTT message. + * + * @param topic The topic. + * @param payload The payload buffer. + * @param payload_length The length of the payload. + */ + bool publish(const std::string &topic, const char *payload, size_t payload_length); + /** Construct and send a JSON MQTT message. * * @param topic The topic. diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 2164b5ca44..f2df6af236 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -99,14 +99,14 @@ bool MQTTCoverComponent::publish_state() { bool success = true; if (traits.get_supports_position()) { 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)) + size_t len = value_accuracy_to_buf(pos, roundf(this->cover_->position * 100), 0); + if (!this->publish(this->get_position_state_topic(), pos, len)) success = false; } if (traits.get_supports_tilt()) { 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)) + size_t len = value_accuracy_to_buf(pos, roundf(this->cover_->tilt * 100), 0); + if (!this->publish(this->get_tilt_state_topic(), pos, len)) success = false; } const char *state_s = this->cover_->current_operation == COVER_OPERATION_OPENING ? "opening" diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index bd6c98b679..a6f0503588 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -174,8 +174,9 @@ bool MQTTFanComponent::publish_state() { } auto traits = this->state_->get_traits(); if (traits.supports_speed()) { - std::string payload = to_string(this->state_->speed); - bool success = this->publish(this->get_speed_level_state_topic(), payload); + char buf[12]; + int len = snprintf(buf, sizeof(buf), "%d", this->state_->speed); + bool success = this->publish(this->get_speed_level_state_topic(), buf, len); failed = failed || !success; } return !failed; diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index cfe6923a5f..c14c889d47 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -80,11 +80,11 @@ bool MQTTSensorComponent::send_initial_state() { } 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"); + return this->publish(this->get_state_topic_(), "None", 4); int8_t accuracy = this->sensor_->get_accuracy_decimals(); char buf[VALUE_ACCURACY_MAX_LEN]; - value_accuracy_to_buf(buf, value, accuracy); - return this->publish(this->get_state_topic_(), buf); + size_t len = value_accuracy_to_buf(buf, value, accuracy); + return this->publish(this->get_state_topic_(), buf, len); } } // namespace esphome::mqtt diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index b4cc367bc5..2faaace46b 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -74,8 +74,8 @@ bool MQTTValveComponent::publish_state() { bool success = true; if (traits.get_supports_position()) { 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)) + size_t len = value_accuracy_to_buf(pos, roundf(this->valve_->position * 100), 0); + if (!this->publish(this->get_position_state_topic(), pos, len)) success = false; } const char *state_s = this->valve_->current_operation == VALVE_OPERATION_OPENING ? "opening"