From 5b5d3fa9b393dee4df06fbe0d84ca77d1df572b5 Mon Sep 17 00:00:00 2001 From: Ryan Wagoner Date: Tue, 17 Feb 2026 14:25:49 -0500 Subject: [PATCH] Add climate preset support to web server REST API The web server's handle_climate_request() parsed mode, fan_mode, and swing_mode but did not parse preset, making it impossible to set climate presets via the REST API or web UI. Also fix the JSON response to always include the presets and custom_presets arrays when the climate entity supports them, rather than only when a preset is currently active. This matches how swing_modes is already handled and ensures the frontend can render preset controls before one is selected. --- esphome/components/web_server/web_server.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 3acd2d2119..e89578a196 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1480,6 +1480,7 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url parse_string_param_(request, ESPHOME_F("mode"), call, &decltype(call)::set_mode); parse_string_param_(request, ESPHOME_F("fan_mode"), call, &decltype(call)::set_fan_mode); parse_string_param_(request, ESPHOME_F("swing_mode"), call, &decltype(call)::set_swing_mode); + parse_string_param_(request, ESPHOME_F("preset"), call, &decltype(call)::set_preset); // Parse temperature parameters // static_cast needed to disambiguate overloaded setters (float vs optional) @@ -1536,12 +1537,12 @@ std::string WebServer::climate_json_(climate::Climate *obj, JsonDetail start_con for (auto swing_mode : traits.get_supported_swing_modes()) opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode))); } - if (traits.get_supports_presets() && obj->preset.has_value()) { + if (traits.get_supports_presets()) { JsonArray opt = root[ESPHOME_F("presets")].to(); for (climate::ClimatePreset m : traits.get_supported_presets()) opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m))); } - if (!traits.get_supported_custom_presets().empty() && obj->has_custom_preset()) { + if (!traits.get_supported_custom_presets().empty()) { JsonArray opt = root[ESPHOME_F("custom_presets")].to(); for (auto const &custom_preset : traits.get_supported_custom_presets()) opt.add(custom_preset);