mirror of
https://github.com/esphome/esphome.git
synced 2026-06-01 01:19:45 +08:00
[mqtt] Use stack buffers for discovery message formatting (#13216)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
@@ -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) {
|
bool CustomMQTTDevice::publish(const std::string &topic, int value) {
|
||||||
char buffer[24];
|
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);
|
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) {
|
bool CustomMQTTDevice::publish_json(const std::string &topic, const json::json_build_t &f, uint8_t qos, bool retain) {
|
||||||
|
|||||||
@@ -98,7 +98,17 @@ void MQTTClientComponent::send_device_info_() {
|
|||||||
uint8_t index = 0;
|
uint8_t index = 0;
|
||||||
for (auto &ip : network::get_ip_addresses()) {
|
for (auto &ip : network::get_ip_addresses()) {
|
||||||
if (ip.is_set()) {
|
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++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,27 +190,37 @@ bool MQTTComponent::send_discovery_() {
|
|||||||
StringRef object_id = this->get_default_object_id_to_(object_id_buf);
|
StringRef object_id = this->get_default_object_id_to_(object_id_buf);
|
||||||
if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
|
if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
|
||||||
char friendly_name_hash[9];
|
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")
|
// Format: mac-component_type-hash (e.g. "aabbccddeeff-sensor-12345678")
|
||||||
// MAC (12) + "-" (1) + domain (max 20) + "-" (1) + hash (8) + null (1) = 43
|
// 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 unique_id[MAC_ADDRESS_BUFFER_SIZE + ESPHOME_DOMAIN_MAX_LEN + 11];
|
||||||
char mac_buf[MAC_ADDRESS_BUFFER_SIZE];
|
char mac_buf[MAC_ADDRESS_BUFFER_SIZE];
|
||||||
get_mac_address_into_buffer(mac_buf);
|
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;
|
root[MQTT_UNIQUE_ID] = unique_id;
|
||||||
} else {
|
} else {
|
||||||
// default to almost-unique ID. It's a hack but the only way to get that
|
// default to almost-unique ID. It's a hack but the only way to get that
|
||||||
// gorgeous device registry view.
|
// 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();
|
const std::string &node_name = App.get_name();
|
||||||
if (discovery_info.object_id_generator == MQTT_DEVICE_NAME_OBJECT_ID_GENERATOR)
|
if (discovery_info.object_id_generator == MQTT_DEVICE_NAME_OBJECT_ID_GENERATOR) {
|
||||||
root[MQTT_OBJECT_ID] = node_name + "_" + object_id.c_str();
|
// 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 &friendly_name_ref = App.get_friendly_name();
|
||||||
const std::string &node_friendly_name = friendly_name_ref.empty() ? node_name : friendly_name_ref;
|
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<JsonObject>();
|
JsonObject device_info = root[MQTT_DEVICE].to<JsonObject>();
|
||||||
char mac[MAC_ADDRESS_BUFFER_SIZE];
|
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 ")";
|
device_info[MQTT_DEVICE_SW_VERSION] = ESPHOME_PROJECT_VERSION " (ESPHome " ESPHOME_VERSION ")";
|
||||||
const char *model = std::strchr(ESPHOME_PROJECT_NAME, '.');
|
const char *model = std::strchr(ESPHOME_PROJECT_NAME, '.');
|
||||||
device_info[MQTT_DEVICE_MODEL] = model == nullptr ? ESPHOME_BOARD : model + 1;
|
device_info[MQTT_DEVICE_MODEL] = model == nullptr ? ESPHOME_BOARD : model + 1;
|
||||||
device_info[MQTT_DEVICE_MANUFACTURER] =
|
if (model == nullptr) {
|
||||||
model == nullptr ? ESPHOME_PROJECT_NAME : std::string(ESPHOME_PROJECT_NAME, model - ESPHOME_PROJECT_NAME);
|
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
|
#else
|
||||||
static const char ver_fmt[] PROGMEM = ESPHOME_VERSION " (config hash 0x%08" PRIx32 ")";
|
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
|
#ifdef USE_ESP8266
|
||||||
char fmt_buf[sizeof(ver_fmt)];
|
snprintf_P(version_buf, sizeof(version_buf), ver_fmt, App.get_config_hash());
|
||||||
strcpy_P(fmt_buf, ver_fmt);
|
|
||||||
const char *fmt = fmt_buf;
|
|
||||||
#else
|
#else
|
||||||
const char *fmt = ver_fmt;
|
snprintf(version_buf, sizeof(version_buf), ver_fmt, App.get_config_hash());
|
||||||
#endif
|
#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;
|
device_info[MQTT_DEVICE_MODEL] = ESPHOME_BOARD;
|
||||||
#if defined(USE_ESP8266) || defined(USE_ESP32)
|
#if defined(USE_ESP8266) || defined(USE_ESP32)
|
||||||
device_info[MQTT_DEVICE_MANUFACTURER] = "Espressif";
|
device_info[MQTT_DEVICE_MANUFACTURER] = "Espressif";
|
||||||
@@ -246,7 +267,7 @@ bool MQTTComponent::send_discovery_() {
|
|||||||
device_info[MQTT_DEVICE_MANUFACTURER] = "Host";
|
device_info[MQTT_DEVICE_MANUFACTURER] = "Host";
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
if (!node_area.empty()) {
|
if (node_area[0] != '\0') {
|
||||||
device_info[MQTT_DEVICE_SUGGESTED_AREA] = node_area;
|
device_info[MQTT_DEVICE_SUGGESTED_AREA] = node_area;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ bool MQTTFanComponent::publish_state() {
|
|||||||
auto traits = this->state_->get_traits();
|
auto traits = this->state_->get_traits();
|
||||||
if (traits.supports_speed()) {
|
if (traits.supports_speed()) {
|
||||||
char buf[12];
|
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);
|
bool success = this->publish(this->get_speed_level_state_topic(), buf, len);
|
||||||
failed = failed || !success;
|
failed = failed || !success;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ bool MQTTNumberComponent::send_initial_state() {
|
|||||||
}
|
}
|
||||||
bool MQTTNumberComponent::publish_state(float value) {
|
bool MQTTNumberComponent::publish_state(float value) {
|
||||||
char buffer[64];
|
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);
|
return this->publish(this->get_state_topic_(), buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user