diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index 158cfb5552..b551f4b8a7 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -300,9 +300,11 @@ const EntityBase *MQTTClimateComponent::get_entity() const { return this->device bool MQTTClimateComponent::publish_state_() { auto traits = this->device_->get_traits(); + // Reusable stack buffer for topic construction (avoids heap allocation per publish) + char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN]; // mode bool success = true; - if (!this->publish(this->get_mode_state_topic(), climate_mode_to_mqtt_str(this->device_->mode))) + if (!this->publish(this->get_mode_state_topic_to(topic_buf), climate_mode_to_mqtt_str(this->device_->mode))) success = false; int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals(); @@ -311,68 +313,70 @@ bool MQTTClimateComponent::publish_state_() { if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE) && !std::isnan(this->device_->current_temperature)) { len = value_accuracy_to_buf(payload, this->device_->current_temperature, current_accuracy); - if (!this->publish(this->get_current_temperature_state_topic(), payload, len)) + if (!this->publish(this->get_current_temperature_state_topic_to(topic_buf), payload, len)) success = false; } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { 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)) + if (!this->publish(this->get_target_temperature_low_state_topic_to(topic_buf), payload, len)) success = false; 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)) + if (!this->publish(this->get_target_temperature_high_state_topic_to(topic_buf), payload, len)) success = false; } else { len = value_accuracy_to_buf(payload, this->device_->target_temperature, target_accuracy); - if (!this->publish(this->get_target_temperature_state_topic(), payload, len)) + if (!this->publish(this->get_target_temperature_state_topic_to(topic_buf), payload, len)) success = false; } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY) && !std::isnan(this->device_->current_humidity)) { len = value_accuracy_to_buf(payload, this->device_->current_humidity, 0); - if (!this->publish(this->get_current_humidity_state_topic(), payload, len)) + if (!this->publish(this->get_current_humidity_state_topic_to(topic_buf), payload, len)) success = false; } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY) && !std::isnan(this->device_->target_humidity)) { len = value_accuracy_to_buf(payload, this->device_->target_humidity, 0); - if (!this->publish(this->get_target_humidity_state_topic(), payload, len)) + if (!this->publish(this->get_target_humidity_state_topic_to(topic_buf), payload, len)) success = false; } if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) { if (this->device_->has_custom_preset()) { - if (!this->publish(this->get_preset_state_topic(), this->device_->get_custom_preset())) + if (!this->publish(this->get_preset_state_topic_to(topic_buf), this->device_->get_custom_preset())) success = false; } else if (this->device_->preset.has_value()) { - if (!this->publish(this->get_preset_state_topic(), climate_preset_to_mqtt_str(this->device_->preset.value()))) + if (!this->publish(this->get_preset_state_topic_to(topic_buf), + climate_preset_to_mqtt_str(this->device_->preset.value()))) success = false; - } else if (!this->publish(this->get_preset_state_topic(), "")) { + } else if (!this->publish(this->get_preset_state_topic_to(topic_buf), "")) { success = false; } } if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { - if (!this->publish(this->get_action_state_topic(), climate_action_to_mqtt_str(this->device_->action))) + if (!this->publish(this->get_action_state_topic_to(topic_buf), climate_action_to_mqtt_str(this->device_->action))) success = false; } if (traits.get_supports_fan_modes()) { if (this->device_->has_custom_fan_mode()) { - if (!this->publish(this->get_fan_mode_state_topic(), this->device_->get_custom_fan_mode())) + if (!this->publish(this->get_fan_mode_state_topic_to(topic_buf), this->device_->get_custom_fan_mode())) success = false; } else if (this->device_->fan_mode.has_value()) { - if (!this->publish(this->get_fan_mode_state_topic(), + if (!this->publish(this->get_fan_mode_state_topic_to(topic_buf), climate_fan_mode_to_mqtt_str(this->device_->fan_mode.value()))) success = false; - } else if (!this->publish(this->get_fan_mode_state_topic(), "")) { + } else if (!this->publish(this->get_fan_mode_state_topic_to(topic_buf), "")) { success = false; } } if (traits.get_supports_swing_modes()) { - if (!this->publish(this->get_swing_mode_state_topic(), climate_swing_mode_to_mqtt_str(this->device_->swing_mode))) + if (!this->publish(this->get_swing_mode_state_topic_to(topic_buf), + climate_swing_mode_to_mqtt_str(this->device_->swing_mode))) success = false; } diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 22c4618b55..7f3671d430 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -55,6 +55,11 @@ void log_mqtt_component(const char *tag, MQTTComponent *obj, bool state_topic, b \ public: \ void set_custom_##name##_##type##_topic(const std::string &topic) { this->custom_##name##_##type##_topic_ = topic; } \ + StringRef get_##name##_##type##_topic_to(std::span buf) const { \ + if (!this->custom_##name##_##type##_topic_.empty()) \ + return StringRef(this->custom_##name##_##type##_topic_.data(), this->custom_##name##_##type##_topic_.size()); \ + return this->get_default_topic_for_to_(buf, #name "/" #type, sizeof(#name "/" #type) - 1); \ + } \ std::string get_##name##_##type##_topic() const { \ if (this->custom_##name##_##type##_topic_.empty()) \ return this->get_default_topic_for_(#name "/" #type); \ diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 50e68ecbcc..c21af413ed 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -112,19 +112,19 @@ bool MQTTCoverComponent::send_initial_state() { return this->publish_state(); } bool MQTTCoverComponent::publish_state() { auto traits = this->cover_->get_traits(); bool success = true; + char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN]; if (traits.get_supports_position()) { char pos[VALUE_ACCURACY_MAX_LEN]; size_t len = value_accuracy_to_buf(pos, roundf(this->cover_->position * 100), 0); - if (!this->publish(this->get_position_state_topic(), pos, len)) + if (!this->publish(this->get_position_state_topic_to(topic_buf), pos, len)) success = false; } if (traits.get_supports_tilt()) { char pos[VALUE_ACCURACY_MAX_LEN]; size_t len = value_accuracy_to_buf(pos, roundf(this->cover_->tilt * 100), 0); - if (!this->publish(this->get_tilt_state_topic(), pos, len)) + if (!this->publish(this->get_tilt_state_topic_to(topic_buf), pos, len)) success = false; } - char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN]; if (!this->publish(this->get_state_topic_to_(topic_buf), cover_state_to_mqtt_str(this->cover_->current_operation, this->cover_->position, traits.get_supports_position()))) diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 84d51895c5..ae2b8c4600 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -173,19 +173,20 @@ bool MQTTFanComponent::publish_state() { this->publish(this->get_state_topic_to_(topic_buf), state_s); bool failed = false; if (this->state_->get_traits().supports_direction()) { - bool success = this->publish(this->get_direction_state_topic(), fan_direction_to_mqtt_str(this->state_->direction)); + bool success = this->publish(this->get_direction_state_topic_to(topic_buf), + fan_direction_to_mqtt_str(this->state_->direction)); failed = failed || !success; } if (this->state_->get_traits().supports_oscillation()) { - bool success = - this->publish(this->get_oscillation_state_topic(), fan_oscillation_to_mqtt_str(this->state_->oscillating)); + bool success = this->publish(this->get_oscillation_state_topic_to(topic_buf), + fan_oscillation_to_mqtt_str(this->state_->oscillating)); failed = failed || !success; } auto traits = this->state_->get_traits(); if (traits.supports_speed()) { char buf[12]; size_t len = buf_append_printf(buf, sizeof(buf), 0, "%d", this->state_->speed); - bool success = this->publish(this->get_speed_level_state_topic(), buf, len); + bool success = this->publish(this->get_speed_level_state_topic_to(topic_buf), buf, len); failed = failed || !success; } return !failed; diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index 16e25f6a8a..2b9f02858b 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -87,13 +87,13 @@ bool MQTTValveComponent::send_initial_state() { return this->publish_state(); } bool MQTTValveComponent::publish_state() { auto traits = this->valve_->get_traits(); bool success = true; + char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN]; if (traits.get_supports_position()) { char pos[VALUE_ACCURACY_MAX_LEN]; size_t len = value_accuracy_to_buf(pos, roundf(this->valve_->position * 100), 0); - if (!this->publish(this->get_position_state_topic(), pos, len)) + if (!this->publish(this->get_position_state_topic_to(topic_buf), pos, len)) success = false; } - char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN]; if (!this->publish(this->get_state_topic_to_(topic_buf), valve_state_to_mqtt_str(this->valve_->current_operation, this->valve_->position, traits.get_supports_position())))