From 4fe2fe80650e3efe80130c1f1bfa22900a7a9660 Mon Sep 17 00:00:00 2001 From: Ryan Wagoner Date: Wed, 18 Feb 2026 13:54:12 -0500 Subject: [PATCH] Always include fan_mode/custom_fan_mode in climate JSON and fix fan_modes trait check Apply Option A (always include with empty string fallback) to fan_mode and custom_fan_mode fields, matching the existing preset pattern. This prevents stale values when SSE updates use Object.assign(). Also fix pre-existing bug where fan_modes list was gated on custom fan modes instead of supports_fan_modes. --- esphome/components/web_server/web_server.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 727aeb8314..1288fb882a 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1521,7 +1521,7 @@ std::string WebServer::climate_json_(climate::Climate *obj, JsonDetail start_con JsonArray opt = root[ESPHOME_F("modes")].to(); for (climate::ClimateMode m : traits.get_supported_modes()) opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m))); - if (!traits.get_supported_custom_fan_modes().empty()) { + if (traits.get_supports_fan_modes()) { JsonArray opt = root[ESPHOME_F("fan_modes")].to(); for (climate::ClimateFanMode m : traits.get_supported_fan_modes()) opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m))); @@ -1562,11 +1562,16 @@ std::string WebServer::climate_json_(climate::Climate *obj, JsonDetail start_con root[ESPHOME_F("state")] = root[ESPHOME_F("action")]; has_state = true; } - if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) { - root[ESPHOME_F("fan_mode")] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value())); + if (traits.get_supports_fan_modes()) { + root[ESPHOME_F("fan_mode")] = + obj->fan_mode.has_value() ? PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value())) : ""; } - if (!traits.get_supported_custom_fan_modes().empty() && obj->has_custom_fan_mode()) { - root[ESPHOME_F("custom_fan_mode")] = obj->get_custom_fan_mode(); + if (!traits.get_supported_custom_fan_modes().empty()) { + if (obj->has_custom_fan_mode()) { + root[ESPHOME_F("custom_fan_mode")] = obj->get_custom_fan_mode(); + } else { + root[ESPHOME_F("custom_fan_mode")] = ""; + } } if (traits.get_supports_presets()) { root[ESPHOME_F("preset")] =