diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index c7d8fb28f0..30e3cfa056 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -987,8 +987,8 @@ message ListEntitiesClimateResponse { string name = 3; reserved 4; // Deprecated: was string unique_id - bool supports_current_temperature = 5; - bool supports_two_point_target_temperature = 6; + bool supports_current_temperature = 5; // Deprecated: use feature_flags + bool supports_two_point_target_temperature = 6; // Deprecated: use feature_flags repeated ClimateMode supported_modes = 7 [(container_pointer) = "std::set"]; float visual_min_temperature = 8; float visual_max_temperature = 9; @@ -997,7 +997,7 @@ message ListEntitiesClimateResponse { // is if CLIMATE_PRESET_AWAY exists is supported_presets // Deprecated in API version 1.5 bool legacy_supports_away = 11 [deprecated=true]; - bool supports_action = 12; + bool supports_action = 12; // Deprecated: use feature_flags repeated ClimateFanMode supported_fan_modes = 13 [(container_pointer) = "std::set"]; repeated ClimateSwingMode supported_swing_modes = 14 [(container_pointer) = "std::set"]; repeated string supported_custom_fan_modes = 15 [(container_pointer) = "std::set"]; @@ -1007,11 +1007,12 @@ message ListEntitiesClimateResponse { string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON"]; EntityCategory entity_category = 20; float visual_current_temperature_step = 21; - bool supports_current_humidity = 22; - bool supports_target_humidity = 23; + bool supports_current_humidity = 22; // Deprecated: use feature_flags + bool supports_target_humidity = 23; // Deprecated: use feature_flags float visual_min_humidity = 24; float visual_max_humidity = 25; uint32 device_id = 26 [(field_ifdef) = "USE_DEVICES"]; + uint32 feature_flags = 27; } message ClimateStateResponse { option (id) = 47; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index ae03dfbb33..1f3456a205 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -27,6 +27,9 @@ #ifdef USE_BLUETOOTH_PROXY #include "esphome/components/bluetooth_proxy/bluetooth_proxy.h" #endif +#ifdef USE_CLIMATE +#include "esphome/components/climate/climate_mode.h" +#endif #ifdef USE_VOICE_ASSISTANT #include "esphome/components/voice_assistant/voice_assistant.h" #endif @@ -623,9 +626,10 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection auto traits = climate->get_traits(); resp.mode = static_cast(climate->mode); resp.action = static_cast(climate->action); - if (traits.get_supports_current_temperature()) + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) resp.current_temperature = climate->current_temperature; - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + climate::CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { resp.target_temperature_low = climate->target_temperature_low; resp.target_temperature_high = climate->target_temperature_high; } else { @@ -644,9 +648,9 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection } if (traits.get_supports_swing_modes()) resp.swing_mode = static_cast(climate->swing_mode); - if (traits.get_supports_current_humidity()) + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) resp.current_humidity = climate->current_humidity; - if (traits.get_supports_target_humidity()) + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) resp.target_humidity = climate->target_humidity; return fill_and_encode_entity_state(climate, resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single); @@ -656,10 +660,14 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection auto *climate = static_cast(entity); ListEntitiesClimateResponse msg; auto traits = climate->get_traits(); + // Flags set for backward compatibility, deprecated in 2025.11.0 msg.supports_current_temperature = traits.get_supports_current_temperature(); msg.supports_current_humidity = traits.get_supports_current_humidity(); msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature(); msg.supports_target_humidity = traits.get_supports_target_humidity(); + msg.supports_action = traits.get_supports_action(); + // Current feature flags and other supported parameters + msg.feature_flags = traits.get_feature_flags(); msg.supported_modes = &traits.get_supported_modes_for_api_(); msg.visual_min_temperature = traits.get_visual_min_temperature(); msg.visual_max_temperature = traits.get_visual_max_temperature(); @@ -667,7 +675,6 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection msg.visual_current_temperature_step = traits.get_visual_current_temperature_step(); msg.visual_min_humidity = traits.get_visual_min_humidity(); msg.visual_max_humidity = traits.get_visual_max_humidity(); - msg.supports_action = traits.get_supports_action(); msg.supported_fan_modes = &traits.get_supported_fan_modes_for_api_(); msg.supported_custom_fan_modes = &traits.get_supported_custom_fan_modes_for_api_(); msg.supported_presets = &traits.get_supported_presets_for_api_(); @@ -1406,7 +1413,7 @@ bool APIConnection::send_hello_response(const HelloRequest &msg) { HelloResponse resp; resp.api_version_major = 1; - resp.api_version_minor = 12; + resp.api_version_minor = 13; // Send only the version string - the client only logs this for debugging and doesn't use it otherwise resp.set_server_info(ESPHOME_VERSION_REF); resp.set_name(StringRef(App.get_name())); diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 70bcf082a6..ee25d4c02d 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -1185,6 +1185,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const { #ifdef USE_DEVICES buffer.encode_uint32(26, this->device_id); #endif + buffer.encode_uint32(27, this->feature_flags); } void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const { size.add_length(1, this->object_id_ref_.size()); @@ -1239,6 +1240,7 @@ void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const { #ifdef USE_DEVICES size.add_uint32(2, this->device_id); #endif + size.add_uint32(2, this->feature_flags); } void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 20866850a9..fca3f546b5 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1369,7 +1369,7 @@ class CameraImageRequest final : public ProtoDecodableMessage { class ListEntitiesClimateResponse final : public InfoResponseProtoMessage { public: static constexpr uint8_t MESSAGE_TYPE = 46; - static constexpr uint8_t ESTIMATED_SIZE = 145; + static constexpr uint8_t ESTIMATED_SIZE = 150; #ifdef HAS_PROTO_MESSAGE_DUMP const char *message_name() const override { return "list_entities_climate_response"; } #endif @@ -1390,6 +1390,7 @@ class ListEntitiesClimateResponse final : public InfoResponseProtoMessage { bool supports_target_humidity{false}; float visual_min_humidity{0.0f}; float visual_max_humidity{0.0f}; + uint32_t feature_flags{0}; void encode(ProtoWriteBuffer buffer) const override; void calculate_size(ProtoSize &size) const override; #ifdef HAS_PROTO_MESSAGE_DUMP diff --git a/esphome/components/api/api_pb2_dump.cpp b/esphome/components/api/api_pb2_dump.cpp index cf732e451b..e803125f53 100644 --- a/esphome/components/api/api_pb2_dump.cpp +++ b/esphome/components/api/api_pb2_dump.cpp @@ -1292,6 +1292,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const { #ifdef USE_DEVICES dump_field(out, "device_id", this->device_id); #endif + dump_field(out, "feature_flags", this->feature_flags); } void ClimateStateResponse::dump_to(std::string &out) const { MessageDumpHelper helper(out, "ClimateStateResponse"); diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index e7a454d459..f3c93ed44e 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -96,7 +96,8 @@ void ClimateCall::validate_() { } if (this->target_temperature_.has_value()) { auto target = *this->target_temperature_; - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { ESP_LOGW(TAG, " Cannot set target temperature for climate device " "with two-point target temperature!"); this->target_temperature_.reset(); @@ -106,7 +107,8 @@ void ClimateCall::validate_() { } } if (this->target_temperature_low_.has_value() || this->target_temperature_high_.has_value()) { - if (!traits.get_supports_two_point_target_temperature()) { + if (!traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { ESP_LOGW(TAG, " Cannot set low/high target temperature for this device!"); this->target_temperature_low_.reset(); this->target_temperature_high_.reset(); @@ -350,13 +352,14 @@ void Climate::save_state_() { state.mode = this->mode; auto traits = this->get_traits(); - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { state.target_temperature_low = this->target_temperature_low; state.target_temperature_high = this->target_temperature_high; } else { state.target_temperature = this->target_temperature; } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { state.target_humidity = this->target_humidity; } if (traits.get_supports_fan_modes() && fan_mode.has_value()) { @@ -400,7 +403,7 @@ void Climate::publish_state() { auto traits = this->get_traits(); ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode))); - if (traits.get_supports_action()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { ESP_LOGD(TAG, " Action: %s", LOG_STR_ARG(climate_action_to_string(this->action))); } if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) { @@ -418,19 +421,20 @@ void Climate::publish_state() { if (traits.get_supports_swing_modes()) { ESP_LOGD(TAG, " Swing Mode: %s", LOG_STR_ARG(climate_swing_mode_to_string(this->swing_mode))); } - if (traits.get_supports_current_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature); } - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { ESP_LOGD(TAG, " Target Temperature: Low: %.2f°C High: %.2f°C", this->target_temperature_low, this->target_temperature_high); } else { ESP_LOGD(TAG, " Target Temperature: %.2f°C", this->target_temperature); } - if (traits.get_supports_current_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { ESP_LOGD(TAG, " Current Humidity: %.0f%%", this->current_humidity); } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { ESP_LOGD(TAG, " Target Humidity: %.0f%%", this->target_humidity); } @@ -485,13 +489,14 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { auto call = climate->make_call(); auto traits = climate->get_traits(); call.set_mode(this->mode); - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { call.set_target_temperature_low(this->target_temperature_low); call.set_target_temperature_high(this->target_temperature_high); } else { call.set_target_temperature(this->target_temperature); } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { call.set_target_humidity(this->target_humidity); } if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) { @@ -508,13 +513,14 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) { void ClimateDeviceRestoreState::apply(Climate *climate) { auto traits = climate->get_traits(); climate->mode = this->mode; - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { climate->target_temperature_low = this->target_temperature_low; climate->target_temperature_high = this->target_temperature_high; } else { climate->target_temperature = this->target_temperature; } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { climate->target_humidity = this->target_humidity; } if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) { @@ -580,28 +586,30 @@ void Climate::dump_traits_(const char *tag) { " Target: %.1f", traits.get_visual_min_temperature(), traits.get_visual_max_temperature(), traits.get_visual_target_temperature_step()); - if (traits.get_supports_current_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step()); } - if (traits.get_supports_target_humidity() || traits.get_supports_current_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY | + climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { ESP_LOGCONFIG(tag, " - Min humidity: %.0f\n" " - Max humidity: %.0f", traits.get_visual_min_humidity(), traits.get_visual_max_humidity()); } - if (traits.get_supports_two_point_target_temperature()) { + if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE | + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE)) { ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature"); } - if (traits.get_supports_current_temperature()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE)) { ESP_LOGCONFIG(tag, " [x] Supports current temperature"); } - if (traits.get_supports_target_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) { ESP_LOGCONFIG(tag, " [x] Supports target humidity"); } - if (traits.get_supports_current_humidity()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY)) { ESP_LOGCONFIG(tag, " [x] Supports current humidity"); } - if (traits.get_supports_action()) { + if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) { ESP_LOGCONFIG(tag, " [x] Supports action"); } if (!traits.get_supported_modes().empty()) { diff --git a/esphome/components/climate/climate_mode.h b/esphome/components/climate/climate_mode.h index 80efb4c048..faec5d2537 100644 --- a/esphome/components/climate/climate_mode.h +++ b/esphome/components/climate/climate_mode.h @@ -98,6 +98,21 @@ enum ClimatePreset : uint8_t { CLIMATE_PRESET_ACTIVITY = 7, }; +enum ClimateFeature : uint32_t { + // Reporting current temperature is supported + CLIMATE_SUPPORTS_CURRENT_TEMPERATURE = 1 << 0, + // Setting two target temperatures is supported (used in conjunction with CLIMATE_MODE_HEAT_COOL) + CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE = 1 << 1, + // Single-point mode is NOT supported (UI always displays two handles, setting 'target_temperature' is not supported) + CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE = 1 << 2, + // Reporting current humidity is supported + CLIMATE_SUPPORTS_CURRENT_HUMIDITY = 1 << 3, + // Setting a target humidity is supported + CLIMATE_SUPPORTS_TARGET_HUMIDITY = 1 << 4, + // Reporting current climate action is supported + CLIMATE_SUPPORTS_ACTION = 1 << 5, +}; + /// Convert the given ClimateMode to a human-readable string. const LogString *climate_mode_to_string(ClimateMode mode); diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index 8bd4714753..50c1e79ad2 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -21,48 +21,92 @@ namespace climate { * - Target Temperature * * All other properties and modes are optional and the integration must mark - * each of them as supported by setting the appropriate flag here. + * each of them as supported by setting the appropriate flag(s) here. * - * - supports current temperature - if the climate device supports reporting a current temperature - * - supports two point target temperature - if the climate device's target temperature should be - * split in target_temperature_low and target_temperature_high instead of just the single target_temperature + * - feature flags: see ClimateFeatures enum in climate_mode.h * - supports modes: * - auto mode (automatic control) * - cool mode (lowers current temperature) * - heat mode (increases current temperature) * - dry mode (removes humidity from air) * - fan mode (only turns on fan) - * - supports action - if the climate device supports reporting the active - * current action of the device with the action property. * - supports fan modes - optionally, if it has a fan which can be configured in different ways: * - on, off, auto, high, medium, low, middle, focus, diffuse, quiet * - supports swing modes - optionally, if it has a swing which can be configured in different ways: * - off, both, vertical, horizontal * * This class also contains static data for the climate device display: - * - visual min/max temperature - tells the frontend what range of temperatures the climate device - * should display (gauge min/max values) + * - visual min/max temperature/humidity - tells the frontend what range of temperature/humidity the + * climate device should display (gauge min/max values) * - temperature step - the step with which to increase/decrease target temperature. * This also affects with how many decimal places the temperature is shown */ class ClimateTraits { public: - bool get_supports_current_temperature() const { return this->supports_current_temperature_; } + /// Get/set feature flags (see ClimateFeatures enum in climate_mode.h) + uint32_t get_feature_flags() const { return this->feature_flags_; } + void add_feature_flags(uint32_t feature_flags) { this->feature_flags_ |= feature_flags; } + void clear_feature_flags(uint32_t feature_flags) { this->feature_flags_ &= ~feature_flags; } + bool has_feature_flags(uint32_t feature_flags) const { return this->feature_flags_ & feature_flags; } + void set_feature_flags(uint32_t feature_flags) { this->feature_flags_ = feature_flags; } + + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_current_temperature() const { + return this->has_feature_flags(CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") void set_supports_current_temperature(bool supports_current_temperature) { - this->supports_current_temperature_ = supports_current_temperature; + if (supports_current_temperature) { + this->add_feature_flags(CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } else { + this->clear_feature_flags(CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + } } - bool get_supports_current_humidity() const { return this->supports_current_humidity_; } + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_current_humidity() const { return this->has_feature_flags(CLIMATE_SUPPORTS_CURRENT_HUMIDITY); } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") void set_supports_current_humidity(bool supports_current_humidity) { - this->supports_current_humidity_ = supports_current_humidity; + if (supports_current_humidity) { + this->add_feature_flags(CLIMATE_SUPPORTS_CURRENT_HUMIDITY); + } else { + this->clear_feature_flags(CLIMATE_SUPPORTS_CURRENT_HUMIDITY); + } } - bool get_supports_two_point_target_temperature() const { return this->supports_two_point_target_temperature_; } + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_two_point_target_temperature() const { + return this->has_feature_flags(CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE); + } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") void set_supports_two_point_target_temperature(bool supports_two_point_target_temperature) { - this->supports_two_point_target_temperature_ = supports_two_point_target_temperature; + if (supports_two_point_target_temperature) + // Use CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE to mimic previous behavior + { + this->add_feature_flags(CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE); + } else { + this->clear_feature_flags(CLIMATE_REQUIRES_TWO_POINT_TARGET_TEMPERATURE); + } } - bool get_supports_target_humidity() const { return this->supports_target_humidity_; } + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_target_humidity() const { return this->has_feature_flags(CLIMATE_SUPPORTS_TARGET_HUMIDITY); } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") void set_supports_target_humidity(bool supports_target_humidity) { - this->supports_target_humidity_ = supports_target_humidity; + if (supports_target_humidity) { + this->add_feature_flags(CLIMATE_SUPPORTS_TARGET_HUMIDITY); + } else { + this->clear_feature_flags(CLIMATE_SUPPORTS_TARGET_HUMIDITY); + } } + ESPDEPRECATED("This method is deprecated, use get_feature_flags() instead", "2025.11.0") + bool get_supports_action() const { return this->has_feature_flags(CLIMATE_SUPPORTS_ACTION); } + ESPDEPRECATED("This method is deprecated, use add_feature_flags() instead", "2025.11.0") + void set_supports_action(bool supports_action) { + if (supports_action) { + this->add_feature_flags(CLIMATE_SUPPORTS_ACTION); + } else { + this->clear_feature_flags(CLIMATE_SUPPORTS_ACTION); + } + } + void set_supported_modes(std::set modes) { this->supported_modes_ = std::move(modes); } void add_supported_mode(ClimateMode mode) { this->supported_modes_.insert(mode); } ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") @@ -82,9 +126,6 @@ class ClimateTraits { bool supports_mode(ClimateMode mode) const { return this->supported_modes_.count(mode); } const std::set &get_supported_modes() const { return this->supported_modes_; } - void set_supports_action(bool supports_action) { this->supports_action_ = supports_action; } - bool get_supports_action() const { return this->supports_action_; } - void set_supported_fan_modes(std::set modes) { this->supported_fan_modes_ = std::move(modes); } void add_supported_fan_mode(ClimateFanMode mode) { this->supported_fan_modes_.insert(mode); } void add_supported_custom_fan_mode(const std::string &mode) { this->supported_custom_fan_modes_.insert(mode); } @@ -219,24 +260,20 @@ class ClimateTraits { } } - bool supports_current_temperature_{false}; - bool supports_current_humidity_{false}; - bool supports_two_point_target_temperature_{false}; - bool supports_target_humidity_{false}; - std::set supported_modes_ = {climate::CLIMATE_MODE_OFF}; - bool supports_action_{false}; - std::set supported_fan_modes_; - std::set supported_swing_modes_; - std::set supported_presets_; - std::set supported_custom_fan_modes_; - std::set supported_custom_presets_; - + uint32_t feature_flags_{0}; float visual_min_temperature_{10}; float visual_max_temperature_{30}; float visual_target_temperature_step_{0.1}; float visual_current_temperature_step_{0.1}; float visual_min_humidity_{30}; float visual_max_humidity_{99}; + + std::set supported_modes_ = {climate::CLIMATE_MODE_OFF}; + std::set supported_fan_modes_; + std::set supported_swing_modes_; + std::set supported_presets_; + std::set supported_custom_fan_modes_; + std::set supported_custom_presets_; }; } // namespace climate diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index e2db3ca5e1..784b3cfb50 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -241,9 +241,14 @@ void ThermostatClimate::control(const climate::ClimateCall &call) { climate::ClimateTraits ThermostatClimate::traits() { auto traits = climate::ClimateTraits(); - traits.set_supports_current_temperature(true); + + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_ACTION | climate::CLIMATE_SUPPORTS_CURRENT_TEMPERATURE); + + if (this->supports_two_points_) + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE); + if (this->humidity_sensor_ != nullptr) - traits.set_supports_current_humidity(true); + traits.add_feature_flags(climate::CLIMATE_SUPPORTS_CURRENT_HUMIDITY); if (this->supports_auto_) traits.add_supported_mode(climate::CLIMATE_MODE_AUTO); @@ -294,9 +299,6 @@ climate::ClimateTraits ThermostatClimate::traits() { for (auto &it : this->custom_preset_config_) { traits.add_supported_custom_preset(it.first); } - - traits.set_supports_two_point_target_temperature(this->supports_two_points_); - traits.set_supports_action(true); return traits; }