From ef64226ed0e3a9d69ae0d086b83ed801ba56f678 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 7 Jan 2026 08:26:21 -1000 Subject: [PATCH] [mqtt] Use ESPHOME_F() for JSON strings to reduce ESP8266 RAM usage (#13049) --- .../mqtt/mqtt_alarm_control_panel.cpp | 12 ++-- esphome/components/mqtt/mqtt_client.cpp | 33 +++++----- esphome/components/mqtt/mqtt_climate.cpp | 62 +++++++++---------- esphome/components/mqtt/mqtt_date.cpp | 18 +++--- esphome/components/mqtt/mqtt_datetime.cpp | 36 +++++------ esphome/components/mqtt/mqtt_light.cpp | 24 +++---- esphome/components/mqtt/mqtt_time.cpp | 18 +++--- esphome/components/mqtt/mqtt_update.cpp | 14 ++--- 8 files changed, 109 insertions(+), 108 deletions(-) diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index 8c570d1472..eb46c3b10c 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -58,22 +58,22 @@ void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendD JsonArray supported_features = root[MQTT_SUPPORTED_FEATURES].to(); const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features(); if (acp_supported_features & ACP_FEAT_ARM_AWAY) { - supported_features.add("arm_away"); + supported_features.add(ESPHOME_F("arm_away")); } if (acp_supported_features & ACP_FEAT_ARM_HOME) { - supported_features.add("arm_home"); + supported_features.add(ESPHOME_F("arm_home")); } if (acp_supported_features & ACP_FEAT_ARM_NIGHT) { - supported_features.add("arm_night"); + supported_features.add(ESPHOME_F("arm_night")); } if (acp_supported_features & ACP_FEAT_ARM_VACATION) { - supported_features.add("arm_vacation"); + supported_features.add(ESPHOME_F("arm_vacation")); } if (acp_supported_features & ACP_FEAT_ARM_CUSTOM_BYPASS) { - supported_features.add("arm_custom_bypass"); + supported_features.add(ESPHOME_F("arm_custom_bypass")); } if (acp_supported_features & ACP_FEAT_TRIGGER) { - supported_features.add("trigger"); + supported_features.add(ESPHOME_F("trigger")); } root[MQTT_CODE_DISARM_REQUIRED] = this->alarm_control_panel_->get_requires_code(); root[MQTT_CODE_ARM_REQUIRED] = this->alarm_control_panel_->get_requires_code_to_arm(); diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index aecf809c8b..652f55734b 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -94,45 +94,46 @@ void MQTTClientComponent::send_device_info_() { index++; } } - root["name"] = App.get_name(); + root[ESPHOME_F("name")] = App.get_name(); if (!App.get_friendly_name().empty()) { - root["friendly_name"] = App.get_friendly_name(); + root[ESPHOME_F("friendly_name")] = App.get_friendly_name(); } #ifdef USE_API - root["port"] = api::global_api_server->get_port(); + root[ESPHOME_F("port")] = api::global_api_server->get_port(); #endif - root["version"] = ESPHOME_VERSION; - root["mac"] = get_mac_address(); + root[ESPHOME_F("version")] = ESPHOME_VERSION; + root[ESPHOME_F("mac")] = get_mac_address(); #ifdef USE_ESP8266 - root["platform"] = "ESP8266"; + root[ESPHOME_F("platform")] = ESPHOME_F("ESP8266"); #endif #ifdef USE_ESP32 - root["platform"] = "ESP32"; + root[ESPHOME_F("platform")] = ESPHOME_F("ESP32"); #endif #ifdef USE_LIBRETINY - root["platform"] = lt_cpu_get_model_name(); + root[ESPHOME_F("platform")] = lt_cpu_get_model_name(); #endif - root["board"] = ESPHOME_BOARD; + root[ESPHOME_F("board")] = ESPHOME_BOARD; #if defined(USE_WIFI) - root["network"] = "wifi"; + root[ESPHOME_F("network")] = ESPHOME_F("wifi"); #elif defined(USE_ETHERNET) - root["network"] = "ethernet"; + root[ESPHOME_F("network")] = ESPHOME_F("ethernet"); #endif #ifdef ESPHOME_PROJECT_NAME - root["project_name"] = ESPHOME_PROJECT_NAME; - root["project_version"] = ESPHOME_PROJECT_VERSION; + root[ESPHOME_F("project_name")] = ESPHOME_PROJECT_NAME; + root[ESPHOME_F("project_version")] = ESPHOME_PROJECT_VERSION; #endif // ESPHOME_PROJECT_NAME #ifdef USE_DASHBOARD_IMPORT - root["package_import_url"] = dashboard_import::get_package_import_url(); + root[ESPHOME_F("package_import_url")] = dashboard_import::get_package_import_url(); #endif #ifdef USE_API_NOISE - root[api::global_api_server->get_noise_ctx().has_psk() ? "api_encryption" : "api_encryption_supported"] = - "Noise_NNpsk0_25519_ChaChaPoly_SHA256"; + root[api::global_api_server->get_noise_ctx().has_psk() ? ESPHOME_F("api_encryption") + : ESPHOME_F("api_encryption_supported")] = + ESPHOME_F("Noise_NNpsk0_25519_ChaChaPoly_SHA256"); #endif }, 2, this->discovery_info_.retain); diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index 9d9ca012a8..d402fff6e6 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -31,18 +31,18 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo JsonArray modes = root[MQTT_MODES].to(); // sort array for nice UI in HA if (traits.supports_mode(CLIMATE_MODE_AUTO)) - modes.add("auto"); - modes.add("off"); + modes.add(ESPHOME_F("auto")); + modes.add(ESPHOME_F("off")); if (traits.supports_mode(CLIMATE_MODE_COOL)) - modes.add("cool"); + modes.add(ESPHOME_F("cool")); if (traits.supports_mode(CLIMATE_MODE_HEAT)) - modes.add("heat"); + modes.add(ESPHOME_F("heat")); if (traits.supports_mode(CLIMATE_MODE_FAN_ONLY)) - modes.add("fan_only"); + modes.add(ESPHOME_F("fan_only")); if (traits.supports_mode(CLIMATE_MODE_DRY)) - modes.add("dry"); + modes.add(ESPHOME_F("dry")); if (traits.supports_mode(CLIMATE_MODE_HEAT_COOL)) - modes.add("heat_cool"); + modes.add(ESPHOME_F("heat_cool")); if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { @@ -90,21 +90,21 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // preset_mode_state_topic root[MQTT_PRESET_MODE_STATE_TOPIC] = this->get_preset_state_topic(); // presets - JsonArray presets = root["preset_modes"].to(); + JsonArray presets = root[ESPHOME_F("preset_modes")].to(); if (traits.supports_preset(CLIMATE_PRESET_HOME)) - presets.add("home"); + presets.add(ESPHOME_F("home")); if (traits.supports_preset(CLIMATE_PRESET_AWAY)) - presets.add("away"); + presets.add(ESPHOME_F("away")); if (traits.supports_preset(CLIMATE_PRESET_BOOST)) - presets.add("boost"); + presets.add(ESPHOME_F("boost")); if (traits.supports_preset(CLIMATE_PRESET_COMFORT)) - presets.add("comfort"); + presets.add(ESPHOME_F("comfort")); if (traits.supports_preset(CLIMATE_PRESET_ECO)) - presets.add("eco"); + presets.add(ESPHOME_F("eco")); if (traits.supports_preset(CLIMATE_PRESET_SLEEP)) - presets.add("sleep"); + presets.add(ESPHOME_F("sleep")); if (traits.supports_preset(CLIMATE_PRESET_ACTIVITY)) - presets.add("activity"); + presets.add(ESPHOME_F("activity")); for (const auto &preset : traits.get_supported_custom_presets()) presets.add(preset); } @@ -120,27 +120,27 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // fan_mode_state_topic root[MQTT_FAN_MODE_STATE_TOPIC] = this->get_fan_mode_state_topic(); // fan_modes - JsonArray fan_modes = root["fan_modes"].to(); + JsonArray fan_modes = root[ESPHOME_F("fan_modes")].to(); if (traits.supports_fan_mode(CLIMATE_FAN_ON)) - fan_modes.add("on"); + fan_modes.add(ESPHOME_F("on")); if (traits.supports_fan_mode(CLIMATE_FAN_OFF)) - fan_modes.add("off"); + fan_modes.add(ESPHOME_F("off")); if (traits.supports_fan_mode(CLIMATE_FAN_AUTO)) - fan_modes.add("auto"); + fan_modes.add(ESPHOME_F("auto")); if (traits.supports_fan_mode(CLIMATE_FAN_LOW)) - fan_modes.add("low"); + fan_modes.add(ESPHOME_F("low")); if (traits.supports_fan_mode(CLIMATE_FAN_MEDIUM)) - fan_modes.add("medium"); + fan_modes.add(ESPHOME_F("medium")); if (traits.supports_fan_mode(CLIMATE_FAN_HIGH)) - fan_modes.add("high"); + fan_modes.add(ESPHOME_F("high")); if (traits.supports_fan_mode(CLIMATE_FAN_MIDDLE)) - fan_modes.add("middle"); + fan_modes.add(ESPHOME_F("middle")); if (traits.supports_fan_mode(CLIMATE_FAN_FOCUS)) - fan_modes.add("focus"); + fan_modes.add(ESPHOME_F("focus")); if (traits.supports_fan_mode(CLIMATE_FAN_DIFFUSE)) - fan_modes.add("diffuse"); + fan_modes.add(ESPHOME_F("diffuse")); if (traits.supports_fan_mode(CLIMATE_FAN_QUIET)) - fan_modes.add("quiet"); + fan_modes.add(ESPHOME_F("quiet")); for (const auto &fan_mode : traits.get_supported_custom_fan_modes()) fan_modes.add(fan_mode); } @@ -151,15 +151,15 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // swing_mode_state_topic root[MQTT_SWING_MODE_STATE_TOPIC] = this->get_swing_mode_state_topic(); // swing_modes - JsonArray swing_modes = root["swing_modes"].to(); + JsonArray swing_modes = root[ESPHOME_F("swing_modes")].to(); if (traits.supports_swing_mode(CLIMATE_SWING_OFF)) - swing_modes.add("off"); + swing_modes.add(ESPHOME_F("off")); if (traits.supports_swing_mode(CLIMATE_SWING_BOTH)) - swing_modes.add("both"); + swing_modes.add(ESPHOME_F("both")); if (traits.supports_swing_mode(CLIMATE_SWING_VERTICAL)) - swing_modes.add("vertical"); + swing_modes.add(ESPHOME_F("vertical")); if (traits.supports_swing_mode(CLIMATE_SWING_HORIZONTAL)) - swing_modes.add("horizontal"); + swing_modes.add(ESPHOME_F("horizontal")); } config.state_topic = false; diff --git a/esphome/components/mqtt/mqtt_date.cpp b/esphome/components/mqtt/mqtt_date.cpp index c5a17abdfd..1715384c5f 100644 --- a/esphome/components/mqtt/mqtt_date.cpp +++ b/esphome/components/mqtt/mqtt_date.cpp @@ -19,14 +19,14 @@ MQTTDateComponent::MQTTDateComponent(DateEntity *date) : date_(date) {} void MQTTDateComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->date_->make_call(); - if (root["year"].is()) { - call.set_year(root["year"]); + if (root[ESPHOME_F("year")].is()) { + call.set_year(root[ESPHOME_F("year")]); } - if (root["month"].is()) { - call.set_month(root["month"]); + if (root[ESPHOME_F("month")].is()) { + call.set_month(root[ESPHOME_F("month")]); } - if (root["day"].is()) { - call.set_day(root["day"]); + if (root[ESPHOME_F("day")].is()) { + call.set_day(root[ESPHOME_F("day")]); } call.perform(); }); @@ -55,9 +55,9 @@ bool MQTTDateComponent::send_initial_state() { bool MQTTDateComponent::publish_state(uint16_t year, uint8_t month, uint8_t day) { return this->publish_json(this->get_state_topic_(), [year, month, day](JsonObject root) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - root["year"] = year; - root["month"] = month; - root["day"] = day; + root[ESPHOME_F("year")] = year; + root[ESPHOME_F("month")] = month; + root[ESPHOME_F("day")] = day; }); } diff --git a/esphome/components/mqtt/mqtt_datetime.cpp b/esphome/components/mqtt/mqtt_datetime.cpp index d2feddcb00..79a2c82180 100644 --- a/esphome/components/mqtt/mqtt_datetime.cpp +++ b/esphome/components/mqtt/mqtt_datetime.cpp @@ -19,23 +19,23 @@ MQTTDateTimeComponent::MQTTDateTimeComponent(DateTimeEntity *datetime) : datetim void MQTTDateTimeComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->datetime_->make_call(); - if (root["year"].is()) { - call.set_year(root["year"]); + if (root[ESPHOME_F("year")].is()) { + call.set_year(root[ESPHOME_F("year")]); } - if (root["month"].is()) { - call.set_month(root["month"]); + if (root[ESPHOME_F("month")].is()) { + call.set_month(root[ESPHOME_F("month")]); } - if (root["day"].is()) { - call.set_day(root["day"]); + if (root[ESPHOME_F("day")].is()) { + call.set_day(root[ESPHOME_F("day")]); } - if (root["hour"].is()) { - call.set_hour(root["hour"]); + if (root[ESPHOME_F("hour")].is()) { + call.set_hour(root[ESPHOME_F("hour")]); } - if (root["minute"].is()) { - call.set_minute(root["minute"]); + if (root[ESPHOME_F("minute")].is()) { + call.set_minute(root[ESPHOME_F("minute")]); } - if (root["second"].is()) { - call.set_second(root["second"]); + if (root[ESPHOME_F("second")].is()) { + call.set_second(root[ESPHOME_F("second")]); } call.perform(); }); @@ -68,12 +68,12 @@ bool MQTTDateTimeComponent::publish_state(uint16_t year, uint8_t month, uint8_t uint8_t second) { return this->publish_json(this->get_state_topic_(), [year, month, day, hour, minute, second](JsonObject root) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - root["year"] = year; - root["month"] = month; - root["day"] = day; - root["hour"] = hour; - root["minute"] = minute; - root["second"] = second; + root[ESPHOME_F("year")] = year; + root[ESPHOME_F("month")] = month; + root[ESPHOME_F("day")] = day; + root[ESPHOME_F("hour")] = hour; + root[ESPHOME_F("minute")] = minute; + root[ESPHOME_F("second")] = second; }); } diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index 6a040e4b1c..0dafe487ff 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -43,33 +43,33 @@ LightState *MQTTJSONLightComponent::get_state() const { return this->state_; } void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - root["schema"] = "json"; + root[ESPHOME_F("schema")] = ESPHOME_F("json"); auto traits = this->state_->get_traits(); root[MQTT_COLOR_MODE] = true; // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - JsonArray color_modes = root["supported_color_modes"].to(); + JsonArray color_modes = root[ESPHOME_F("supported_color_modes")].to(); if (traits.supports_color_mode(ColorMode::ON_OFF)) - color_modes.add("onoff"); + color_modes.add(ESPHOME_F("onoff")); if (traits.supports_color_mode(ColorMode::BRIGHTNESS)) - color_modes.add("brightness"); + color_modes.add(ESPHOME_F("brightness")); if (traits.supports_color_mode(ColorMode::WHITE)) - color_modes.add("white"); + color_modes.add(ESPHOME_F("white")); if (traits.supports_color_mode(ColorMode::COLOR_TEMPERATURE) || traits.supports_color_mode(ColorMode::COLD_WARM_WHITE)) - color_modes.add("color_temp"); + color_modes.add(ESPHOME_F("color_temp")); if (traits.supports_color_mode(ColorMode::RGB)) - color_modes.add("rgb"); + color_modes.add(ESPHOME_F("rgb")); if (traits.supports_color_mode(ColorMode::RGB_WHITE) || // HA doesn't support RGBCT, and there's no CWWW->CT emulation in ESPHome yet, so ignore CT control for now traits.supports_color_mode(ColorMode::RGB_COLOR_TEMPERATURE)) - color_modes.add("rgbw"); + color_modes.add(ESPHOME_F("rgbw")); if (traits.supports_color_mode(ColorMode::RGB_COLD_WARM_WHITE)) - color_modes.add("rgbww"); + color_modes.add(ESPHOME_F("rgbww")); // legacy API if (traits.supports_color_capability(ColorCapability::BRIGHTNESS)) - root["brightness"] = true; + root[ESPHOME_F("brightness")] = true; if (traits.supports_color_mode(ColorMode::COLOR_TEMPERATURE) || traits.supports_color_mode(ColorMode::COLD_WARM_WHITE)) { @@ -78,11 +78,11 @@ void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscovery } if (this->state_->supports_effects()) { - root["effect"] = true; + root[ESPHOME_F("effect")] = true; JsonArray effect_list = root[MQTT_EFFECT_LIST].to(); for (auto *effect : this->state_->get_effects()) effect_list.add(effect->get_name()); - effect_list.add("None"); + effect_list.add(ESPHOME_F("None")); } } bool MQTTJSONLightComponent::send_initial_state() { return this->publish_state_(); } diff --git a/esphome/components/mqtt/mqtt_time.cpp b/esphome/components/mqtt/mqtt_time.cpp index c97a463858..01b8dd3483 100644 --- a/esphome/components/mqtt/mqtt_time.cpp +++ b/esphome/components/mqtt/mqtt_time.cpp @@ -19,14 +19,14 @@ MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {} void MQTTTimeComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->time_->make_call(); - if (root["hour"].is()) { - call.set_hour(root["hour"]); + if (root[ESPHOME_F("hour")].is()) { + call.set_hour(root[ESPHOME_F("hour")]); } - if (root["minute"].is()) { - call.set_minute(root["minute"]); + if (root[ESPHOME_F("minute")].is()) { + call.set_minute(root[ESPHOME_F("minute")]); } - if (root["second"].is()) { - call.set_second(root["second"]); + if (root[ESPHOME_F("second")].is()) { + call.set_second(root[ESPHOME_F("second")]); } call.perform(); }); @@ -55,9 +55,9 @@ bool MQTTTimeComponent::send_initial_state() { bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) { return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - root["hour"] = hour; - root["minute"] = minute; - root["second"] = second; + root[ESPHOME_F("hour")] = hour; + root[ESPHOME_F("minute")] = minute; + root[ESPHOME_F("second")] = second; }); } diff --git a/esphome/components/mqtt/mqtt_update.cpp b/esphome/components/mqtt/mqtt_update.cpp index 150ddbf745..aedf2414c1 100644 --- a/esphome/components/mqtt/mqtt_update.cpp +++ b/esphome/components/mqtt/mqtt_update.cpp @@ -29,20 +29,20 @@ void MQTTUpdateComponent::setup() { bool MQTTUpdateComponent::publish_state() { return this->publish_json(this->get_state_topic_(), [this](JsonObject root) { - root["installed_version"] = this->update_->update_info.current_version; - root["latest_version"] = this->update_->update_info.latest_version; - root["title"] = this->update_->update_info.title; + root[ESPHOME_F("installed_version")] = this->update_->update_info.current_version; + root[ESPHOME_F("latest_version")] = this->update_->update_info.latest_version; + root[ESPHOME_F("title")] = this->update_->update_info.title; if (!this->update_->update_info.summary.empty()) - root["release_summary"] = this->update_->update_info.summary; + root[ESPHOME_F("release_summary")] = this->update_->update_info.summary; if (!this->update_->update_info.release_url.empty()) - root["release_url"] = this->update_->update_info.release_url; + root[ESPHOME_F("release_url")] = this->update_->update_info.release_url; }); } void MQTTUpdateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson - root["schema"] = "json"; - root[MQTT_PAYLOAD_INSTALL] = "INSTALL"; + root[ESPHOME_F("schema")] = ESPHOME_F("json"); + root[MQTT_PAYLOAD_INSTALL] = ESPHOME_F("INSTALL"); } bool MQTTUpdateComponent::send_initial_state() { return this->publish_state(); }