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.
This commit is contained in:
Ryan Wagoner
2026-02-18 13:54:12 -05:00
parent 38fc007a6a
commit 4fe2fe8065

View File

@@ -1521,7 +1521,7 @@ std::string WebServer::climate_json_(climate::Climate *obj, JsonDetail start_con
JsonArray opt = root[ESPHOME_F("modes")].to<JsonArray>();
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<JsonArray>();
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")] =