From b08419fa473533e28a7cd86f98b2b3f98f1b7f27 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Nov 2025 12:30:45 -0600 Subject: [PATCH] [mqtt] Use StringRef to avoid string copies in discovery (#11731) --- .../mqtt/mqtt_alarm_control_panel.cpp | 2 +- .../components/mqtt/mqtt_binary_sensor.cpp | 9 ++++--- esphome/components/mqtt/mqtt_button.cpp | 7 ++--- esphome/components/mqtt/mqtt_component.cpp | 26 +++++++++++-------- esphome/components/mqtt/mqtt_component.h | 8 +++--- esphome/components/mqtt/mqtt_cover.cpp | 9 ++++--- esphome/components/mqtt/mqtt_event.cpp | 8 ++++-- esphome/components/mqtt/mqtt_fan.cpp | 14 +++++----- esphome/components/mqtt/mqtt_lock.cpp | 2 +- esphome/components/mqtt/mqtt_number.cpp | 14 +++++++--- esphome/components/mqtt/mqtt_sensor.cpp | 14 ++++++---- esphome/components/mqtt/mqtt_switch.cpp | 2 +- esphome/components/mqtt/mqtt_text_sensor.cpp | 8 +++--- esphome/components/mqtt/mqtt_update.cpp | 2 +- esphome/components/mqtt/mqtt_valve.cpp | 8 +++--- 15 files changed, 81 insertions(+), 52 deletions(-) diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index 94460c31a7..dd3df5f8aa 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -36,7 +36,7 @@ void MQTTAlarmControlPanelComponent::setup() { } else if (strcasecmp(payload.c_str(), "TRIGGERED") == 0) { call.triggered(); } else { - ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name_().c_str(), payload.c_str()); } call.perform(); }); diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index 2ce4928574..479cee205a 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -30,9 +30,12 @@ MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor } void MQTTBinarySensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->binary_sensor_->get_device_class().empty()) - root[MQTT_DEVICE_CLASS] = this->binary_sensor_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->binary_sensor_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) if (this->binary_sensor_->is_status_binary_sensor()) root[MQTT_PAYLOAD_ON] = mqtt::global_mqtt_client->get_availability().payload_available; if (this->binary_sensor_->is_status_binary_sensor()) diff --git a/esphome/components/mqtt/mqtt_button.cpp b/esphome/components/mqtt/mqtt_button.cpp index b3435edf38..f8eb0eab2d 100644 --- a/esphome/components/mqtt/mqtt_button.cpp +++ b/esphome/components/mqtt/mqtt_button.cpp @@ -20,7 +20,7 @@ void MQTTButtonComponent::setup() { if (payload == "PRESS") { this->button_->press(); } else { - ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str()); this->status_momentary_warning("state", 5000); } }); @@ -33,8 +33,9 @@ void MQTTButtonComponent::dump_config() { void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson config.state_topic = false; - if (!this->button_->get_device_class().empty()) { - root[MQTT_DEVICE_CLASS] = this->button_->get_device_class(); + const auto device_class = this->button_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; } // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index eb6114008a..1cd818964e 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -64,11 +64,11 @@ bool MQTTComponent::send_discovery_() { const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info(); if (discovery_info.clean) { - ESP_LOGV(TAG, "'%s': Cleaning discovery", this->friendly_name().c_str()); + ESP_LOGV(TAG, "'%s': Cleaning discovery", this->friendly_name_().c_str()); return global_mqtt_client->publish(this->get_discovery_topic_(discovery_info), "", 0, this->qos_, true); } - ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str()); + ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name_().c_str()); // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return global_mqtt_client->publish_json( @@ -85,12 +85,16 @@ bool MQTTComponent::send_discovery_() { } // Fields from EntityBase - root[MQTT_NAME] = this->get_entity()->has_own_name() ? this->friendly_name() : ""; + root[MQTT_NAME] = this->get_entity()->has_own_name() ? this->friendly_name_() : ""; - if (this->is_disabled_by_default()) + if (this->is_disabled_by_default_()) root[MQTT_ENABLED_BY_DEFAULT] = false; - if (!this->get_icon().empty()) - root[MQTT_ICON] = this->get_icon(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto icon_ref = this->get_icon_ref_(); + if (!icon_ref.empty()) { + root[MQTT_ICON] = icon_ref; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) const auto entity_category = this->get_entity()->get_entity_category(); switch (entity_category) { @@ -122,7 +126,7 @@ bool MQTTComponent::send_discovery_() { const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info(); if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) { char friendly_name_hash[9]; - sprintf(friendly_name_hash, "%08" PRIx32, fnv1_hash(this->friendly_name())); + sprintf(friendly_name_hash, "%08" PRIx32, fnv1_hash(this->friendly_name_())); friendly_name_hash[8] = 0; // ensure the hash-string ends with null root[MQTT_UNIQUE_ID] = get_mac_address() + "-" + this->component_type() + "-" + friendly_name_hash; } else { @@ -184,7 +188,7 @@ bool MQTTComponent::is_discovery_enabled() const { } std::string MQTTComponent::get_default_object_id_() const { - return str_sanitize(str_snake_case(this->friendly_name())); + return str_sanitize(str_snake_case(this->friendly_name_())); } void MQTTComponent::subscribe(const std::string &topic, mqtt_callback_t callback, uint8_t qos) { @@ -268,9 +272,9 @@ void MQTTComponent::schedule_resend_state() { this->resend_state_ = true; } bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connected(); } // Pull these properties from EntityBase if not overridden -std::string MQTTComponent::friendly_name() const { return this->get_entity()->get_name(); } -std::string MQTTComponent::get_icon() const { return this->get_entity()->get_icon(); } -bool MQTTComponent::is_disabled_by_default() const { return this->get_entity()->is_disabled_by_default(); } +std::string MQTTComponent::friendly_name_() const { return this->get_entity()->get_name(); } +StringRef MQTTComponent::get_icon_ref_() const { return this->get_entity()->get_icon_ref(); } +bool MQTTComponent::is_disabled_by_default_() const { return this->get_entity()->is_disabled_by_default(); } bool MQTTComponent::is_internal() { if (this->has_custom_state_topic_) { // If the custom state_topic is null, return true as it is internal and should not publish diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 851fdd842c..2f8dfcf64e 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -165,13 +165,13 @@ class MQTTComponent : public Component { virtual const EntityBase *get_entity() const = 0; /// Get the friendly name of this MQTT component. - virtual std::string friendly_name() const; + std::string friendly_name_() const; - /// Get the icon field of this component - virtual std::string get_icon() const; + /// Get the icon field of this component as StringRef + StringRef get_icon_ref_() const; /// Get whether the underlying Entity is disabled by default - virtual bool is_disabled_by_default() const; + bool is_disabled_by_default_() const; /// Get the MQTT topic that new states will be shared to. std::string get_state_topic_() const; diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index 6fb61ee469..b63aa66d29 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -67,9 +67,12 @@ void MQTTCoverComponent::dump_config() { } } void MQTTCoverComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->cover_->get_device_class().empty()) - root[MQTT_DEVICE_CLASS] = this->cover_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->cover_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) auto traits = this->cover_->get_traits(); if (traits.get_is_assumed_state()) { diff --git a/esphome/components/mqtt/mqtt_event.cpp b/esphome/components/mqtt/mqtt_event.cpp index f972d545c6..e206335446 100644 --- a/esphome/components/mqtt/mqtt_event.cpp +++ b/esphome/components/mqtt/mqtt_event.cpp @@ -21,8 +21,12 @@ void MQTTEventComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConf for (const auto &event_type : this->event_->get_event_types()) event_types.add(event_type); - if (!this->event_->get_device_class().empty()) - root[MQTT_DEVICE_CLASS] = this->event_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->event_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) config.command_topic = false; } diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index 70e1ae3b4a..2aefc3a4db 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -24,15 +24,15 @@ void MQTTFanComponent::setup() { auto val = parse_on_off(payload.c_str()); switch (val) { case PARSE_ON: - ESP_LOGD(TAG, "'%s' Turning Fan ON.", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s' Turning Fan ON.", this->friendly_name_().c_str()); this->state_->turn_on().perform(); break; case PARSE_OFF: - ESP_LOGD(TAG, "'%s' Turning Fan OFF.", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s' Turning Fan OFF.", this->friendly_name_().c_str()); this->state_->turn_off().perform(); break; case PARSE_TOGGLE: - ESP_LOGD(TAG, "'%s' Toggling Fan.", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s' Toggling Fan.", this->friendly_name_().c_str()); this->state_->toggle().perform(); break; case PARSE_NONE: @@ -48,11 +48,11 @@ void MQTTFanComponent::setup() { auto val = parse_on_off(payload.c_str(), "forward", "reverse"); switch (val) { case PARSE_ON: - ESP_LOGD(TAG, "'%s': Setting direction FORWARD", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s': Setting direction FORWARD", this->friendly_name_().c_str()); this->state_->make_call().set_direction(fan::FanDirection::FORWARD).perform(); break; case PARSE_OFF: - ESP_LOGD(TAG, "'%s': Setting direction REVERSE", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s': Setting direction REVERSE", this->friendly_name_().c_str()); this->state_->make_call().set_direction(fan::FanDirection::REVERSE).perform(); break; case PARSE_TOGGLE: @@ -75,11 +75,11 @@ void MQTTFanComponent::setup() { auto val = parse_on_off(payload.c_str(), "oscillate_on", "oscillate_off"); switch (val) { case PARSE_ON: - ESP_LOGD(TAG, "'%s': Setting oscillating ON", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s': Setting oscillating ON", this->friendly_name_().c_str()); this->state_->make_call().set_oscillating(true).perform(); break; case PARSE_OFF: - ESP_LOGD(TAG, "'%s': Setting oscillating OFF", this->friendly_name().c_str()); + ESP_LOGD(TAG, "'%s': Setting oscillating OFF", this->friendly_name_().c_str()); this->state_->make_call().set_oscillating(false).perform(); break; case PARSE_TOGGLE: diff --git a/esphome/components/mqtt/mqtt_lock.cpp b/esphome/components/mqtt/mqtt_lock.cpp index 0412624983..0e15377ba4 100644 --- a/esphome/components/mqtt/mqtt_lock.cpp +++ b/esphome/components/mqtt/mqtt_lock.cpp @@ -24,7 +24,7 @@ void MQTTLockComponent::setup() { } else if (strcasecmp(payload.c_str(), "OPEN") == 0) { this->lock_->open(); } else { - ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str()); this->status_momentary_warning("state", 5000); } }); diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index a44632ff30..f419eac130 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -44,8 +44,11 @@ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon root[MQTT_MIN] = traits.get_min_value(); root[MQTT_MAX] = traits.get_max_value(); root[MQTT_STEP] = traits.get_step(); - if (!this->number_->traits.get_unit_of_measurement().empty()) - root[MQTT_UNIT_OF_MEASUREMENT] = this->number_->traits.get_unit_of_measurement(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto unit_of_measurement = this->number_->traits.get_unit_of_measurement_ref(); + if (!unit_of_measurement.empty()) { + root[MQTT_UNIT_OF_MEASUREMENT] = unit_of_measurement; + } switch (this->number_->traits.get_mode()) { case NUMBER_MODE_AUTO: break; @@ -56,8 +59,11 @@ void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon root[MQTT_MODE] = "slider"; break; } - if (!this->number_->traits.get_device_class().empty()) - root[MQTT_DEVICE_CLASS] = this->number_->traits.get_device_class(); + const auto device_class = this->number_->traits.get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) config.command_topic = true; } diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index 9e61f6ef3b..010ac3013e 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -44,13 +44,17 @@ void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; } void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->sensor_->get_device_class().empty()) { - root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->sensor_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; } - if (!this->sensor_->get_unit_of_measurement().empty()) - root[MQTT_UNIT_OF_MEASUREMENT] = this->sensor_->get_unit_of_measurement(); + const auto unit_of_measurement = this->sensor_->get_unit_of_measurement_ref(); + if (!unit_of_measurement.empty()) { + root[MQTT_UNIT_OF_MEASUREMENT] = unit_of_measurement; + } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) if (this->get_expire_after() > 0) root[MQTT_EXPIRE_AFTER] = this->get_expire_after() / 1000; diff --git a/esphome/components/mqtt/mqtt_switch.cpp b/esphome/components/mqtt/mqtt_switch.cpp index 8b1323bdb2..b3a35420b9 100644 --- a/esphome/components/mqtt/mqtt_switch.cpp +++ b/esphome/components/mqtt/mqtt_switch.cpp @@ -29,7 +29,7 @@ void MQTTSwitchComponent::setup() { break; case PARSE_NONE: default: - ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown status payload: %s", this->friendly_name_().c_str(), payload.c_str()); this->status_momentary_warning("state", 5000); break; } diff --git a/esphome/components/mqtt/mqtt_text_sensor.cpp b/esphome/components/mqtt/mqtt_text_sensor.cpp index 42260ed2a8..e6e7cf04e8 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.cpp +++ b/esphome/components/mqtt/mqtt_text_sensor.cpp @@ -15,10 +15,12 @@ using namespace esphome::text_sensor; MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : sensor_(sensor) {} void MQTTTextSensor::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->sensor_->get_device_class().empty()) { - root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->sensor_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) config.command_topic = false; } void MQTTTextSensor::setup() { diff --git a/esphome/components/mqtt/mqtt_update.cpp b/esphome/components/mqtt/mqtt_update.cpp index 5d4807c7f3..20f3a69a9e 100644 --- a/esphome/components/mqtt/mqtt_update.cpp +++ b/esphome/components/mqtt/mqtt_update.cpp @@ -20,7 +20,7 @@ void MQTTUpdateComponent::setup() { if (payload == "INSTALL") { this->update_->perform(); } else { - ESP_LOGW(TAG, "'%s': Received unknown update payload: %s", this->friendly_name().c_str(), payload.c_str()); + ESP_LOGW(TAG, "'%s': Received unknown update payload: %s", this->friendly_name_().c_str(), payload.c_str()); this->status_momentary_warning("state", 5000); } }); diff --git a/esphome/components/mqtt/mqtt_valve.cpp b/esphome/components/mqtt/mqtt_valve.cpp index 551398cf42..ae60670748 100644 --- a/esphome/components/mqtt/mqtt_valve.cpp +++ b/esphome/components/mqtt/mqtt_valve.cpp @@ -49,10 +49,12 @@ void MQTTValveComponent::dump_config() { } } void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - if (!this->valve_->get_device_class().empty()) { - root[MQTT_DEVICE_CLASS] = this->valve_->get_device_class(); + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + const auto device_class = this->valve_->get_device_class_ref(); + if (!device_class.empty()) { + root[MQTT_DEVICE_CLASS] = device_class; } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) auto traits = this->valve_->get_traits(); if (traits.get_is_assumed_state()) {