diff --git a/esphome/components/mqtt/custom_mqtt_device.cpp b/esphome/components/mqtt/custom_mqtt_device.cpp index 7ff65bb42c..64521f5cf3 100644 --- a/esphome/components/mqtt/custom_mqtt_device.cpp +++ b/esphome/components/mqtt/custom_mqtt_device.cpp @@ -18,7 +18,7 @@ bool CustomMQTTDevice::publish(const std::string &topic, float value, int8_t num } bool CustomMQTTDevice::publish(const std::string &topic, int value) { char buffer[24]; - int len = snprintf(buffer, sizeof(buffer), "%d", value); + size_t len = buf_append_printf(buffer, sizeof(buffer), 0, "%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) { diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index de79b61358..e7364f3406 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -98,7 +98,17 @@ void MQTTClientComponent::send_device_info_() { uint8_t index = 0; for (auto &ip : network::get_ip_addresses()) { if (ip.is_set()) { - root["ip" + (index == 0 ? "" : esphome::to_string(index))] = ip.str(); + char key[8]; // "ip" + up to 3 digits + null + char ip_buf[network::IP_ADDRESS_BUFFER_SIZE]; + if (index == 0) { + key[0] = 'i'; + key[1] = 'p'; + key[2] = '\0'; + } else { + buf_append_printf(key, sizeof(key), 0, "ip%u", index); + } + ip.str_to(ip_buf); + root[key] = ip_buf; index++; } } diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 2ba466af3b..cb8b92cad0 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -190,27 +190,37 @@ bool MQTTComponent::send_discovery_() { StringRef object_id = this->get_default_object_id_to_(object_id_buf); if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) { char friendly_name_hash[9]; - snprintf(friendly_name_hash, sizeof(friendly_name_hash), "%08" PRIx32, fnv1_hash(this->friendly_name_())); + buf_append_printf(friendly_name_hash, sizeof(friendly_name_hash), 0, "%08" PRIx32, + fnv1_hash(this->friendly_name_())); // Format: mac-component_type-hash (e.g. "aabbccddeeff-sensor-12345678") // MAC (12) + "-" (1) + domain (max 20) + "-" (1) + hash (8) + null (1) = 43 char unique_id[MAC_ADDRESS_BUFFER_SIZE + ESPHOME_DOMAIN_MAX_LEN + 11]; char mac_buf[MAC_ADDRESS_BUFFER_SIZE]; get_mac_address_into_buffer(mac_buf); - snprintf(unique_id, sizeof(unique_id), "%s-%s-%s", mac_buf, this->component_type(), friendly_name_hash); + buf_append_printf(unique_id, sizeof(unique_id), 0, "%s-%s-%s", mac_buf, this->component_type(), + friendly_name_hash); root[MQTT_UNIQUE_ID] = unique_id; } else { // default to almost-unique ID. It's a hack but the only way to get that // gorgeous device registry view. - root[MQTT_UNIQUE_ID] = "ESP" + std::string(this->component_type()) + object_id.c_str(); + // "ESP" (3) + component_type (max 20) + object_id (max 128) + null + char unique_id_buf[3 + MQTT_COMPONENT_TYPE_MAX_LEN + OBJECT_ID_MAX_LEN + 1]; + buf_append_printf(unique_id_buf, sizeof(unique_id_buf), 0, "ESP%s%s", this->component_type(), + object_id.c_str()); + root[MQTT_UNIQUE_ID] = unique_id_buf; } const std::string &node_name = App.get_name(); - if (discovery_info.object_id_generator == MQTT_DEVICE_NAME_OBJECT_ID_GENERATOR) - root[MQTT_OBJECT_ID] = node_name + "_" + object_id.c_str(); + if (discovery_info.object_id_generator == MQTT_DEVICE_NAME_OBJECT_ID_GENERATOR) { + // node_name (max 31) + "_" (1) + object_id (max 128) + null + char object_id_full[ESPHOME_DEVICE_NAME_MAX_LEN + 1 + OBJECT_ID_MAX_LEN + 1]; + buf_append_printf(object_id_full, sizeof(object_id_full), 0, "%s_%s", node_name.c_str(), object_id.c_str()); + root[MQTT_OBJECT_ID] = object_id_full; + } const std::string &friendly_name_ref = App.get_friendly_name(); const std::string &node_friendly_name = friendly_name_ref.empty() ? node_name : friendly_name_ref; - std::string node_area = App.get_area(); + const char *node_area = App.get_area(); JsonObject device_info = root[MQTT_DEVICE].to(); char mac[MAC_ADDRESS_BUFFER_SIZE]; @@ -221,18 +231,29 @@ bool MQTTComponent::send_discovery_() { device_info[MQTT_DEVICE_SW_VERSION] = ESPHOME_PROJECT_VERSION " (ESPHome " ESPHOME_VERSION ")"; const char *model = std::strchr(ESPHOME_PROJECT_NAME, '.'); device_info[MQTT_DEVICE_MODEL] = model == nullptr ? ESPHOME_BOARD : model + 1; - device_info[MQTT_DEVICE_MANUFACTURER] = - model == nullptr ? ESPHOME_PROJECT_NAME : std::string(ESPHOME_PROJECT_NAME, model - ESPHOME_PROJECT_NAME); + if (model == nullptr) { + device_info[MQTT_DEVICE_MANUFACTURER] = ESPHOME_PROJECT_NAME; + } else { + // Extract manufacturer (part before '.') using stack buffer to avoid heap allocation + // memcpy is used instead of strncpy since we know the exact length and strncpy + // would still require manual null-termination + char manufacturer[sizeof(ESPHOME_PROJECT_NAME)]; + size_t len = model - ESPHOME_PROJECT_NAME; + memcpy(manufacturer, ESPHOME_PROJECT_NAME, len); + manufacturer[len] = '\0'; + device_info[MQTT_DEVICE_MANUFACTURER] = manufacturer; + } #else static const char ver_fmt[] PROGMEM = ESPHOME_VERSION " (config hash 0x%08" PRIx32 ")"; + // Buffer sized for format string expansion: ~4 bytes net growth from format specifier to 8 hex digits, plus + // safety margin + char version_buf[sizeof(ver_fmt) + 8]; #ifdef USE_ESP8266 - char fmt_buf[sizeof(ver_fmt)]; - strcpy_P(fmt_buf, ver_fmt); - const char *fmt = fmt_buf; + snprintf_P(version_buf, sizeof(version_buf), ver_fmt, App.get_config_hash()); #else - const char *fmt = ver_fmt; + snprintf(version_buf, sizeof(version_buf), ver_fmt, App.get_config_hash()); #endif - device_info[MQTT_DEVICE_SW_VERSION] = str_sprintf(fmt, App.get_config_hash()); + device_info[MQTT_DEVICE_SW_VERSION] = version_buf; device_info[MQTT_DEVICE_MODEL] = ESPHOME_BOARD; #if defined(USE_ESP8266) || defined(USE_ESP32) device_info[MQTT_DEVICE_MANUFACTURER] = "Espressif"; @@ -246,7 +267,7 @@ bool MQTTComponent::send_discovery_() { device_info[MQTT_DEVICE_MANUFACTURER] = "Host"; #endif #endif - if (!node_area.empty()) { + if (node_area[0] != '\0') { device_info[MQTT_DEVICE_SUGGESTED_AREA] = node_area; } diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index a6f0503588..0909090023 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -175,7 +175,7 @@ bool MQTTFanComponent::publish_state() { auto traits = this->state_->get_traits(); if (traits.supports_speed()) { char buf[12]; - int len = snprintf(buf, sizeof(buf), "%d", this->state_->speed); + 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); failed = failed || !success; } diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index 8342210ee4..471c0d1208 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -75,7 +75,7 @@ bool MQTTNumberComponent::send_initial_state() { } bool MQTTNumberComponent::publish_state(float value) { char buffer[64]; - snprintf(buffer, sizeof(buffer), "%f", value); + buf_append_printf(buffer, sizeof(buffer), 0, "%f", value); return this->publish(this->get_state_topic_(), buffer); }