mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	climate: add support for quiet fan mode (#3609)
This commit is contained in:
		| @@ -787,6 +787,7 @@ enum ClimateFanMode { | ||||
|   CLIMATE_FAN_MIDDLE = 6; | ||||
|   CLIMATE_FAN_FOCUS = 7; | ||||
|   CLIMATE_FAN_DIFFUSE = 8; | ||||
|   CLIMATE_FAN_QUIET = 9; | ||||
| } | ||||
| enum ClimateSwingMode { | ||||
|   CLIMATE_SWING_OFF = 0; | ||||
|   | ||||
| @@ -235,6 +235,8 @@ template<> const char *proto_enum_to_string<enums::ClimateFanMode>(enums::Climat | ||||
|       return "CLIMATE_FAN_FOCUS"; | ||||
|     case enums::CLIMATE_FAN_DIFFUSE: | ||||
|       return "CLIMATE_FAN_DIFFUSE"; | ||||
|     case enums::CLIMATE_FAN_QUIET: | ||||
|       return "CLIMATE_FAN_QUIET"; | ||||
|     default: | ||||
|       return "UNKNOWN"; | ||||
|   } | ||||
|   | ||||
| @@ -99,6 +99,7 @@ enum ClimateFanMode : uint32_t { | ||||
|   CLIMATE_FAN_MIDDLE = 6, | ||||
|   CLIMATE_FAN_FOCUS = 7, | ||||
|   CLIMATE_FAN_DIFFUSE = 8, | ||||
|   CLIMATE_FAN_QUIET = 9, | ||||
| }; | ||||
| enum ClimateSwingMode : uint32_t { | ||||
|   CLIMATE_SWING_OFF = 0, | ||||
|   | ||||
| @@ -73,6 +73,7 @@ CLIMATE_FAN_MODES = { | ||||
|     "MIDDLE": ClimateFanMode.CLIMATE_FAN_MIDDLE, | ||||
|     "FOCUS": ClimateFanMode.CLIMATE_FAN_FOCUS, | ||||
|     "DIFFUSE": ClimateFanMode.CLIMATE_FAN_DIFFUSE, | ||||
|     "QUIET": ClimateFanMode.CLIMATE_FAN_QUIET, | ||||
| } | ||||
|  | ||||
| validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True) | ||||
|   | ||||
| @@ -174,6 +174,8 @@ ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_FOCUS); | ||||
|   } else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_DIFFUSE); | ||||
|   } else if (str_equals_case_insensitive(fan_mode, "QUIET")) { | ||||
|     this->set_fan_mode(CLIMATE_FAN_QUIET); | ||||
|   } else { | ||||
|     if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) { | ||||
|       this->custom_fan_mode_ = fan_mode; | ||||
|   | ||||
| @@ -62,6 +62,8 @@ const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) { | ||||
|       return LOG_STR("FOCUS"); | ||||
|     case climate::CLIMATE_FAN_DIFFUSE: | ||||
|       return LOG_STR("DIFFUSE"); | ||||
|     case climate::CLIMATE_FAN_QUIET: | ||||
|       return LOG_STR("QUIET"); | ||||
|     default: | ||||
|       return LOG_STR("UNKNOWN"); | ||||
|   } | ||||
|   | ||||
| @@ -62,6 +62,8 @@ enum ClimateFanMode : uint8_t { | ||||
|   CLIMATE_FAN_FOCUS = 7, | ||||
|   /// The fan mode is set to Diffuse | ||||
|   CLIMATE_FAN_DIFFUSE = 8, | ||||
|   /// The fan mode is set to Quiet | ||||
|   CLIMATE_FAN_QUIET = 9, | ||||
| }; | ||||
|  | ||||
| /// Enum for all modes a climate swing can be in | ||||
|   | ||||
| @@ -28,7 +28,7 @@ namespace climate { | ||||
|  *  - 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 | ||||
|  *    - 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 | ||||
|  * | ||||
|   | ||||
| @@ -111,6 +111,7 @@ class DemoClimate : public climate::Climate, public Component { | ||||
|             climate::CLIMATE_FAN_MIDDLE, | ||||
|             climate::CLIMATE_FAN_FOCUS, | ||||
|             climate::CLIMATE_FAN_DIFFUSE, | ||||
|             climate::CLIMATE_FAN_QUIET, | ||||
|         }); | ||||
|         traits.set_supported_custom_fan_modes({"Auto Low", "Auto High"}); | ||||
|         traits.set_supported_swing_modes({ | ||||
|   | ||||
| @@ -102,6 +102,8 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo | ||||
|       fan_modes.add("focus"); | ||||
|     if (traits.supports_fan_mode(CLIMATE_FAN_DIFFUSE)) | ||||
|       fan_modes.add("diffuse"); | ||||
|     if (traits.supports_fan_mode(CLIMATE_FAN_QUIET)) | ||||
|       fan_modes.add("quiet"); | ||||
|     for (const auto &fan_mode : traits.get_supported_custom_fan_modes()) | ||||
|       fan_modes.add(fan_mode); | ||||
|   } | ||||
| @@ -328,6 +330,9 @@ bool MQTTClimateComponent::publish_state_() { | ||||
|         case CLIMATE_FAN_DIFFUSE: | ||||
|           payload = "diffuse"; | ||||
|           break; | ||||
|         case CLIMATE_FAN_QUIET: | ||||
|           payload = "quiet"; | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|     if (this->device_->custom_fan_mode.has_value()) | ||||
|   | ||||
| @@ -24,6 +24,7 @@ from esphome.const import ( | ||||
|     CONF_FAN_MODE_MIDDLE_ACTION, | ||||
|     CONF_FAN_MODE_FOCUS_ACTION, | ||||
|     CONF_FAN_MODE_DIFFUSE_ACTION, | ||||
|     CONF_FAN_MODE_QUIET_ACTION, | ||||
|     CONF_FAN_ONLY_ACTION, | ||||
|     CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER, | ||||
|     CONF_FAN_ONLY_COOLING, | ||||
| @@ -273,6 +274,7 @@ def validate_thermostat(config): | ||||
|             CONF_FAN_MODE_MIDDLE_ACTION, | ||||
|             CONF_FAN_MODE_FOCUS_ACTION, | ||||
|             CONF_FAN_MODE_DIFFUSE_ACTION, | ||||
|             CONF_FAN_MODE_QUIET_ACTION, | ||||
|         ], | ||||
|     } | ||||
|     for req_config_item, config_triggers in requirements.items(): | ||||
| @@ -413,6 +415,7 @@ def validate_thermostat(config): | ||||
|             "MIDDLE": [CONF_FAN_MODE_MIDDLE_ACTION], | ||||
|             "FOCUS": [CONF_FAN_MODE_FOCUS_ACTION], | ||||
|             "DIFFUSE": [CONF_FAN_MODE_DIFFUSE_ACTION], | ||||
|             "QUIET": [CONF_FAN_MODE_QUIET_ACTION], | ||||
|         } | ||||
|  | ||||
|         for preset_config in config[CONF_PRESET]: | ||||
| @@ -500,12 +503,13 @@ def validate_thermostat(config): | ||||
|             CONF_FAN_MODE_MIDDLE_ACTION, | ||||
|             CONF_FAN_MODE_FOCUS_ACTION, | ||||
|             CONF_FAN_MODE_DIFFUSE_ACTION, | ||||
|             CONF_FAN_MODE_QUIET_ACTION, | ||||
|         ] | ||||
|         for config_req_action in requirements: | ||||
|             if config_req_action in config: | ||||
|                 return config | ||||
|         raise cv.Invalid( | ||||
|             f"At least one of {CONF_FAN_MODE_ON_ACTION}, {CONF_FAN_MODE_OFF_ACTION}, {CONF_FAN_MODE_AUTO_ACTION}, {CONF_FAN_MODE_LOW_ACTION}, {CONF_FAN_MODE_MEDIUM_ACTION}, {CONF_FAN_MODE_HIGH_ACTION}, {CONF_FAN_MODE_MIDDLE_ACTION}, {CONF_FAN_MODE_FOCUS_ACTION}, {CONF_FAN_MODE_DIFFUSE_ACTION} must be defined to use {CONF_MIN_FAN_MODE_SWITCHING_TIME}" | ||||
|             f"At least one of {CONF_FAN_MODE_ON_ACTION}, {CONF_FAN_MODE_OFF_ACTION}, {CONF_FAN_MODE_AUTO_ACTION}, {CONF_FAN_MODE_LOW_ACTION}, {CONF_FAN_MODE_MEDIUM_ACTION}, {CONF_FAN_MODE_HIGH_ACTION}, {CONF_FAN_MODE_MIDDLE_ACTION}, {CONF_FAN_MODE_FOCUS_ACTION}, {CONF_FAN_MODE_DIFFUSE_ACTION}, {CONF_FAN_MODE_QUIET_ACTION} must be defined to use {CONF_MIN_FAN_MODE_SWITCHING_TIME}" | ||||
|         ) | ||||
|     return config | ||||
|  | ||||
| @@ -563,6 +567,9 @@ CONFIG_SCHEMA = cv.All( | ||||
|             cv.Optional(CONF_FAN_MODE_DIFFUSE_ACTION): automation.validate_automation( | ||||
|                 single=True | ||||
|             ), | ||||
|             cv.Optional(CONF_FAN_MODE_QUIET_ACTION): automation.validate_automation( | ||||
|                 single=True | ||||
|             ), | ||||
|             cv.Optional(CONF_SWING_BOTH_ACTION): automation.validate_automation( | ||||
|                 single=True | ||||
|             ), | ||||
| @@ -836,6 +843,11 @@ async def to_code(config): | ||||
|             var.get_fan_mode_diffuse_trigger(), [], config[CONF_FAN_MODE_DIFFUSE_ACTION] | ||||
|         ) | ||||
|         cg.add(var.set_supports_fan_mode_diffuse(True)) | ||||
|     if CONF_FAN_MODE_QUIET_ACTION in config: | ||||
|         await automation.build_automation( | ||||
|             var.get_fan_mode_quiet_trigger(), [], config[CONF_FAN_MODE_QUIET_ACTION] | ||||
|         ) | ||||
|         cg.add(var.set_supports_fan_mode_quiet(True)) | ||||
|     if CONF_SWING_BOTH_ACTION in config: | ||||
|         await automation.build_automation( | ||||
|             var.get_swing_mode_both_trigger(), [], config[CONF_SWING_BOTH_ACTION] | ||||
|   | ||||
| @@ -247,6 +247,8 @@ climate::ClimateTraits ThermostatClimate::traits() { | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_FOCUS); | ||||
|   if (supports_fan_mode_diffuse_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_DIFFUSE); | ||||
|   if (supports_fan_mode_quiet_) | ||||
|     traits.add_supported_fan_mode(climate::CLIMATE_FAN_QUIET); | ||||
|  | ||||
|   if (supports_swing_mode_both_) | ||||
|     traits.add_supported_swing_mode(climate::CLIMATE_SWING_BOTH); | ||||
| @@ -594,6 +596,10 @@ void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bo | ||||
|         trig = this->fan_mode_diffuse_trigger_; | ||||
|         ESP_LOGVV(TAG, "Switching to FAN_DIFFUSE mode"); | ||||
|         break; | ||||
|       case climate::CLIMATE_FAN_QUIET: | ||||
|         trig = this->fan_mode_quiet_trigger_; | ||||
|         ESP_LOGVV(TAG, "Switching to FAN_QUIET mode"); | ||||
|         break; | ||||
|       default: | ||||
|         // we cannot report an invalid mode back to HA (even if it asked for one) | ||||
|         //  and must assume some valid value | ||||
| @@ -1093,6 +1099,7 @@ ThermostatClimate::ThermostatClimate() | ||||
|       fan_mode_middle_trigger_(new Trigger<>()), | ||||
|       fan_mode_focus_trigger_(new Trigger<>()), | ||||
|       fan_mode_diffuse_trigger_(new Trigger<>()), | ||||
|       fan_mode_quiet_trigger_(new Trigger<>()), | ||||
|       swing_mode_both_trigger_(new Trigger<>()), | ||||
|       swing_mode_off_trigger_(new Trigger<>()), | ||||
|       swing_mode_horizontal_trigger_(new Trigger<>()), | ||||
| @@ -1208,6 +1215,9 @@ void ThermostatClimate::set_supports_fan_mode_focus(bool supports_fan_mode_focus | ||||
| void ThermostatClimate::set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse) { | ||||
|   this->supports_fan_mode_diffuse_ = supports_fan_mode_diffuse; | ||||
| } | ||||
| void ThermostatClimate::set_supports_fan_mode_quiet(bool supports_fan_mode_quiet) { | ||||
|   this->supports_fan_mode_quiet_ = supports_fan_mode_quiet; | ||||
| } | ||||
| void ThermostatClimate::set_supports_swing_mode_both(bool supports_swing_mode_both) { | ||||
|   this->supports_swing_mode_both_ = supports_swing_mode_both; | ||||
| } | ||||
| @@ -1250,6 +1260,7 @@ Trigger<> *ThermostatClimate::get_fan_mode_high_trigger() const { return this->f | ||||
| Trigger<> *ThermostatClimate::get_fan_mode_middle_trigger() const { return this->fan_mode_middle_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_fan_mode_focus_trigger() const { return this->fan_mode_focus_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_fan_mode_diffuse_trigger() const { return this->fan_mode_diffuse_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_fan_mode_quiet_trigger() const { return this->fan_mode_quiet_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_swing_mode_both_trigger() const { return this->swing_mode_both_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_swing_mode_off_trigger() const { return this->swing_mode_off_trigger_; } | ||||
| Trigger<> *ThermostatClimate::get_swing_mode_horizontal_trigger() const { return this->swing_mode_horizontal_trigger_; } | ||||
| @@ -1294,7 +1305,8 @@ void ThermostatClimate::dump_config() { | ||||
|   } | ||||
|   if (this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ || | ||||
|       this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ || | ||||
|       this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_) { | ||||
|       this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_ || | ||||
|       this->supports_fan_mode_quiet_) { | ||||
|     ESP_LOGCONFIG(TAG, "  Minimum Fan Mode Switching Time: %us", | ||||
|                   this->timer_duration_(thermostat::TIMER_FAN_MODE) / 1000); | ||||
|   } | ||||
| @@ -1323,6 +1335,7 @@ void ThermostatClimate::dump_config() { | ||||
|   ESP_LOGCONFIG(TAG, "  Supports FAN MODE MIDDLE: %s", YESNO(this->supports_fan_mode_middle_)); | ||||
|   ESP_LOGCONFIG(TAG, "  Supports FAN MODE FOCUS: %s", YESNO(this->supports_fan_mode_focus_)); | ||||
|   ESP_LOGCONFIG(TAG, "  Supports FAN MODE DIFFUSE: %s", YESNO(this->supports_fan_mode_diffuse_)); | ||||
|   ESP_LOGCONFIG(TAG, "  Supports FAN MODE QUIET: %s", YESNO(this->supports_fan_mode_quiet_)); | ||||
|   ESP_LOGCONFIG(TAG, "  Supports SWING MODE BOTH: %s", YESNO(this->supports_swing_mode_both_)); | ||||
|   ESP_LOGCONFIG(TAG, "  Supports SWING MODE OFF: %s", YESNO(this->supports_swing_mode_off_)); | ||||
|   ESP_LOGCONFIG(TAG, "  Supports SWING MODE HORIZONTAL: %s", YESNO(this->supports_swing_mode_horizontal_)); | ||||
|   | ||||
| @@ -101,6 +101,7 @@ class ThermostatClimate : public climate::Climate, public Component { | ||||
|   void set_supports_fan_mode_middle(bool supports_fan_mode_middle); | ||||
|   void set_supports_fan_mode_focus(bool supports_fan_mode_focus); | ||||
|   void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse); | ||||
|   void set_supports_fan_mode_quiet(bool supports_fan_mode_quiet); | ||||
|   void set_supports_swing_mode_both(bool supports_swing_mode_both); | ||||
|   void set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal); | ||||
|   void set_supports_swing_mode_off(bool supports_swing_mode_off); | ||||
| @@ -132,6 +133,7 @@ class ThermostatClimate : public climate::Climate, public Component { | ||||
|   Trigger<> *get_fan_mode_middle_trigger() const; | ||||
|   Trigger<> *get_fan_mode_focus_trigger() const; | ||||
|   Trigger<> *get_fan_mode_diffuse_trigger() const; | ||||
|   Trigger<> *get_fan_mode_quiet_trigger() const; | ||||
|   Trigger<> *get_swing_mode_both_trigger() const; | ||||
|   Trigger<> *get_swing_mode_horizontal_trigger() const; | ||||
|   Trigger<> *get_swing_mode_off_trigger() const; | ||||
| @@ -277,6 +279,7 @@ class ThermostatClimate : public climate::Climate, public Component { | ||||
|   bool supports_fan_mode_middle_{false}; | ||||
|   bool supports_fan_mode_focus_{false}; | ||||
|   bool supports_fan_mode_diffuse_{false}; | ||||
|   bool supports_fan_mode_quiet_{false}; | ||||
|  | ||||
|   /// Whether the controller supports various swing modes. | ||||
|   /// | ||||
| @@ -372,6 +375,9 @@ class ThermostatClimate : public climate::Climate, public Component { | ||||
|   /// The trigger to call when the controller should switch the fan to "diffuse" position. | ||||
|   Trigger<> *fan_mode_diffuse_trigger_{nullptr}; | ||||
|  | ||||
|   /// The trigger to call when the controller should switch the fan to "quiet" position. | ||||
|   Trigger<> *fan_mode_quiet_trigger_{nullptr}; | ||||
|  | ||||
|   /// The trigger to call when the controller should switch the swing mode to "both". | ||||
|   Trigger<> *swing_mode_both_trigger_{nullptr}; | ||||
|  | ||||
|   | ||||
| @@ -237,6 +237,7 @@ CONF_FAN_MODE_MEDIUM_ACTION = "fan_mode_medium_action" | ||||
| CONF_FAN_MODE_MIDDLE_ACTION = "fan_mode_middle_action" | ||||
| CONF_FAN_MODE_OFF_ACTION = "fan_mode_off_action" | ||||
| CONF_FAN_MODE_ON_ACTION = "fan_mode_on_action" | ||||
| CONF_FAN_MODE_QUIET_ACTION = "fan_mode_quiet_action" | ||||
| CONF_FAN_MODE_STATE_TOPIC = "fan_mode_state_topic" | ||||
| CONF_FAN_ONLY_ACTION = "fan_only_action" | ||||
| CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER = "fan_only_action_uses_fan_mode_timer" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user