mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Haier climate integration update (#7416)
Co-authored-by: Pavlo Dudnytskyi <pdudnytskyi@astrata.eu>
This commit is contained in:
		| @@ -166,6 +166,7 @@ esphome/components/haier/* @paveldn | ||||
| esphome/components/haier/binary_sensor/* @paveldn | ||||
| esphome/components/haier/button/* @paveldn | ||||
| esphome/components/haier/sensor/* @paveldn | ||||
| esphome/components/haier/switch/* @paveldn | ||||
| esphome/components/haier/text_sensor/* @paveldn | ||||
| esphome/components/havells_solar/* @sourabhjaiswal | ||||
| esphome/components/hbridge/fan/* @WeekendWarrior | ||||
|   | ||||
| @@ -114,7 +114,6 @@ SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = { | ||||
| SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = { | ||||
|     "AWAY": ClimatePreset.CLIMATE_PRESET_AWAY, | ||||
|     "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, | ||||
|     "ECO": ClimatePreset.CLIMATE_PRESET_ECO, | ||||
|     "SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP, | ||||
| } | ||||
|  | ||||
| @@ -240,7 +239,9 @@ CONFIG_SCHEMA = cv.All( | ||||
|                     ): cv.ensure_list( | ||||
|                         cv.enum(SUPPORTED_HON_CONTROL_METHODS, upper=True) | ||||
|                     ), | ||||
|                     cv.Optional(CONF_BEEPER, default=True): cv.boolean, | ||||
|                     cv.Optional(CONF_BEEPER): cv.invalid( | ||||
|                         f"The {CONF_BEEPER} option is deprecated, use beeper_on/beeper_off actions or beeper switch for a haier platform instead" | ||||
|                     ), | ||||
|                     cv.Optional( | ||||
|                         CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE | ||||
|                     ): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50), | ||||
| @@ -254,7 +255,7 @@ CONFIG_SCHEMA = cv.All( | ||||
|                     ): cv.int_range(min=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE), | ||||
|                     cv.Optional( | ||||
|                         CONF_SUPPORTED_PRESETS, | ||||
|                         default=["BOOST", "ECO", "SLEEP"],  # No AWAY by default | ||||
|                         default=["BOOST", "SLEEP"],  # No AWAY by default | ||||
|                     ): cv.ensure_list( | ||||
|                         cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True) | ||||
|                     ), | ||||
|   | ||||
| @@ -52,8 +52,6 @@ bool check_timeout(std::chrono::steady_clock::time_point now, std::chrono::stead | ||||
| HaierClimateBase::HaierClimateBase() | ||||
|     : haier_protocol_(*this), | ||||
|       protocol_phase_(ProtocolPhases::SENDING_INIT_1), | ||||
|       display_status_(true), | ||||
|       health_mode_(false), | ||||
|       force_send_control_(false), | ||||
|       forced_request_status_(false), | ||||
|       reset_protocol_request_(false), | ||||
| @@ -127,21 +125,34 @@ haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_() { | ||||
| } | ||||
| #endif | ||||
|  | ||||
| bool HaierClimateBase::get_display_state() const { return this->display_status_; } | ||||
| void HaierClimateBase::save_settings() { | ||||
|   HaierBaseSettings settings{this->get_health_mode(), this->get_display_state()}; | ||||
|   if (!this->base_rtc_.save(&settings)) { | ||||
|     ESP_LOGW(TAG, "Failed to save settings"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool HaierClimateBase::get_display_state() const { | ||||
|   return (this->display_status_ == SwitchState::ON) || (this->display_status_ == SwitchState::PENDING_ON); | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::set_display_state(bool state) { | ||||
|   if (this->display_status_ != state) { | ||||
|     this->display_status_ = state; | ||||
|   if (state != this->get_display_state()) { | ||||
|     this->display_status_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; | ||||
|     this->force_send_control_ = true; | ||||
|     this->save_settings(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool HaierClimateBase::get_health_mode() const { return this->health_mode_; } | ||||
| bool HaierClimateBase::get_health_mode() const { | ||||
|   return (this->health_mode_ == SwitchState::ON) || (this->health_mode_ == SwitchState::PENDING_ON); | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::set_health_mode(bool state) { | ||||
|   if (this->health_mode_ != state) { | ||||
|     this->health_mode_ = state; | ||||
|   if (state != this->get_health_mode()) { | ||||
|     this->health_mode_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; | ||||
|     this->force_send_control_ = true; | ||||
|     this->save_settings(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -287,6 +298,14 @@ void HaierClimateBase::loop() { | ||||
|   } | ||||
|   this->process_phase(now); | ||||
|   this->haier_protocol_.loop(); | ||||
| #ifdef USE_SWITCH | ||||
|   if ((this->display_switch_ != nullptr) && (this->display_switch_->state != this->get_display_state())) { | ||||
|     this->display_switch_->publish_state(this->get_display_state()); | ||||
|   } | ||||
|   if ((this->health_mode_switch_ != nullptr) && (this->health_mode_switch_->state != this->get_health_mode())) { | ||||
|     this->health_mode_switch_->publish_state(this->get_health_mode()); | ||||
|   } | ||||
| #endif  // USE_SWITCH | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::process_protocol_reset() { | ||||
| @@ -329,6 +348,26 @@ bool HaierClimateBase::prepare_pending_action() { | ||||
|  | ||||
| ClimateTraits HaierClimateBase::traits() { return traits_; } | ||||
|  | ||||
| void HaierClimateBase::initialization() { | ||||
|   constexpr uint32_t restore_settings_version = 0xA77D21EF; | ||||
|   this->base_rtc_ = | ||||
|       global_preferences->make_preference<HaierBaseSettings>(this->get_object_id_hash() ^ restore_settings_version); | ||||
|   HaierBaseSettings recovered; | ||||
|   if (!this->base_rtc_.load(&recovered)) { | ||||
|     recovered = {false, true}; | ||||
|   } | ||||
|   this->display_status_ = recovered.display_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; | ||||
|   this->health_mode_ = recovered.health_mode ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; | ||||
| #ifdef USE_SWITCH | ||||
|   if (this->display_switch_ != nullptr) { | ||||
|     this->display_switch_->publish_state(this->get_display_state()); | ||||
|   } | ||||
|   if (this->health_mode_switch_ != nullptr) { | ||||
|     this->health_mode_switch_->publish_state(this->get_health_mode()); | ||||
|   } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::control(const ClimateCall &call) { | ||||
|   ESP_LOGD("Control", "Control call"); | ||||
|   if (!this->valid_connection()) { | ||||
| @@ -353,6 +392,22 @@ void HaierClimateBase::control(const ClimateCall &call) { | ||||
|   } | ||||
| } | ||||
|  | ||||
| #ifdef USE_SWITCH | ||||
| void HaierClimateBase::set_display_switch(switch_::Switch *sw) { | ||||
|   this->display_switch_ = sw; | ||||
|   if ((this->display_switch_ != nullptr) && (this->valid_connection())) { | ||||
|     this->display_switch_->publish_state(this->get_display_state()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void HaierClimateBase::set_health_mode_switch(switch_::Switch *sw) { | ||||
|   this->health_mode_switch_ = sw; | ||||
|   if ((this->health_mode_switch_ != nullptr) && (this->valid_connection())) { | ||||
|     this->health_mode_switch_->publish_state(this->get_health_mode()); | ||||
|   } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| void HaierClimateBase::HvacSettings::reset() { | ||||
|   this->valid = false; | ||||
|   this->mode.reset(); | ||||
|   | ||||
| @@ -8,6 +8,10 @@ | ||||
| // HaierProtocol | ||||
| #include <protocol/haier_protocol.h> | ||||
|  | ||||
| #ifdef USE_SWITCH | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| @@ -20,10 +24,24 @@ enum class ActionRequest : uint8_t { | ||||
|   START_STERI_CLEAN = 5,  // only hOn | ||||
| }; | ||||
|  | ||||
| struct HaierBaseSettings { | ||||
|   bool health_mode; | ||||
|   bool display_state; | ||||
| }; | ||||
|  | ||||
| class HaierClimateBase : public esphome::Component, | ||||
|                          public esphome::climate::Climate, | ||||
|                          public esphome::uart::UARTDevice, | ||||
|                          public haier_protocol::ProtocolStream { | ||||
| #ifdef USE_SWITCH | ||||
|  public: | ||||
|   void set_display_switch(switch_::Switch *sw); | ||||
|   void set_health_mode_switch(switch_::Switch *sw); | ||||
|  | ||||
|  protected: | ||||
|   switch_::Switch *display_switch_{nullptr}; | ||||
|   switch_::Switch *health_mode_switch_{nullptr}; | ||||
| #endif | ||||
|  public: | ||||
|   HaierClimateBase(); | ||||
|   HaierClimateBase(const HaierClimateBase &) = delete; | ||||
| @@ -82,7 +100,8 @@ class HaierClimateBase : public esphome::Component, | ||||
|   virtual void process_phase(std::chrono::steady_clock::time_point now) = 0; | ||||
|   virtual haier_protocol::HaierMessage get_control_message() = 0;          // NOLINT(readability-identifier-naming) | ||||
|   virtual haier_protocol::HaierMessage get_power_message(bool state) = 0;  // NOLINT(readability-identifier-naming) | ||||
|   virtual void initialization(){}; | ||||
|   virtual void save_settings(); | ||||
|   virtual void initialization(); | ||||
|   virtual bool prepare_pending_action(); | ||||
|   virtual void process_protocol_reset(); | ||||
|   esphome::climate::ClimateTraits traits() override; | ||||
| @@ -127,13 +146,19 @@ class HaierClimateBase : public esphome::Component, | ||||
|     ActionRequest action; | ||||
|     esphome::optional<haier_protocol::HaierMessage> message; | ||||
|   }; | ||||
|   enum class SwitchState { | ||||
|     OFF = 0b00, | ||||
|     ON = 0b01, | ||||
|     PENDING_OFF = 0b10, | ||||
|     PENDING_ON = 0b11, | ||||
|   }; | ||||
|   haier_protocol::ProtocolHandler haier_protocol_; | ||||
|   ProtocolPhases protocol_phase_; | ||||
|   esphome::optional<PendingAction> action_request_; | ||||
|   uint8_t fan_mode_speed_; | ||||
|   uint8_t other_modes_fan_speed_; | ||||
|   bool display_status_; | ||||
|   bool health_mode_; | ||||
|   SwitchState display_status_{SwitchState::ON}; | ||||
|   SwitchState health_mode_{SwitchState::OFF}; | ||||
|   bool force_send_control_; | ||||
|   bool forced_request_status_; | ||||
|   bool reset_protocol_request_; | ||||
| @@ -148,6 +173,7 @@ class HaierClimateBase : public esphome::Component, | ||||
|   std::chrono::steady_clock::time_point last_status_request_;          // To request AC status | ||||
|   std::chrono::steady_clock::time_point last_signal_request_;          // To send WiFI signal level | ||||
|   CallbackManager<void(const char *, size_t)> status_message_callback_{}; | ||||
|   ESPPreferenceObject base_rtc_; | ||||
| }; | ||||
|  | ||||
| class StatusMessageTrigger : public Trigger<const char *, size_t> { | ||||
|   | ||||
| @@ -31,9 +31,32 @@ HonClimate::HonClimate() | ||||
|  | ||||
| HonClimate::~HonClimate() {} | ||||
|  | ||||
| void HonClimate::set_beeper_state(bool state) { this->beeper_status_ = state; } | ||||
| void HonClimate::set_beeper_state(bool state) { | ||||
|   if (state != this->settings_.beeper_state) { | ||||
|     this->settings_.beeper_state = state; | ||||
| #ifdef USE_SWITCH | ||||
|     this->beeper_switch_->publish_state(state); | ||||
| #endif | ||||
|     this->hon_rtc_.save(&this->settings_); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool HonClimate::get_beeper_state() const { return this->beeper_status_; } | ||||
| bool HonClimate::get_beeper_state() const { return this->settings_.beeper_state; } | ||||
|  | ||||
| void HonClimate::set_quiet_mode_state(bool state) { | ||||
|   if (state != this->get_quiet_mode_state()) { | ||||
|     this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; | ||||
|     this->settings_.quiet_mode_state = state; | ||||
| #ifdef USE_SWITCH | ||||
|     this->quiet_mode_switch_->publish_state(state); | ||||
| #endif | ||||
|     this->hon_rtc_.save(&this->settings_); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool HonClimate::get_quiet_mode_state() const { | ||||
|   return (this->quiet_mode_state_ == SwitchState::ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON); | ||||
| } | ||||
|  | ||||
| esphome::optional<hon_protocol::VerticalSwingMode> HonClimate::get_vertical_airflow() const { | ||||
|   return this->current_vertical_swing_; | ||||
| @@ -474,16 +497,19 @@ haier_protocol::HaierMessage HonClimate::get_power_message(bool state) { | ||||
| } | ||||
|  | ||||
| void HonClimate::initialization() { | ||||
|   constexpr uint32_t restore_settings_version = 0xE834D8DCUL; | ||||
|   this->rtc_ = global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version); | ||||
|   HaierClimateBase::initialization(); | ||||
|   constexpr uint32_t restore_settings_version = 0x57EB59DDUL; | ||||
|   this->hon_rtc_ = | ||||
|       global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version); | ||||
|   HonSettings recovered; | ||||
|   if (this->rtc_.load(&recovered)) { | ||||
|   if (this->hon_rtc_.load(&recovered)) { | ||||
|     this->settings_ = recovered; | ||||
|   } else { | ||||
|     this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER}; | ||||
|     this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER, true, false}; | ||||
|   } | ||||
|   this->current_vertical_swing_ = this->settings_.last_vertiacal_swing; | ||||
|   this->current_horizontal_swing_ = this->settings_.last_horizontal_swing; | ||||
|   this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; | ||||
| } | ||||
|  | ||||
| haier_protocol::HaierMessage HonClimate::get_control_message() { | ||||
| @@ -519,8 +545,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | ||||
|           out_data->ac_power = 1; | ||||
|           out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::FAN; | ||||
|           out_data->fan_mode = this->fan_mode_speed_;  // Auto doesn't work in fan only mode | ||||
|           // Disabling boost and eco mode for Fan only | ||||
|           out_data->quiet_mode = 0; | ||||
|           // Disabling boost for Fan only | ||||
|           out_data->fast_mode = 0; | ||||
|           break; | ||||
|         case CLIMATE_MODE_COOL: | ||||
| @@ -582,47 +607,34 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | ||||
|     } | ||||
|     if (out_data->ac_power == 0) { | ||||
|       // If AC is off - no presets allowed | ||||
|       out_data->quiet_mode = 0; | ||||
|       out_data->fast_mode = 0; | ||||
|       out_data->sleep_mode = 0; | ||||
|     } else if (climate_control.preset.has_value()) { | ||||
|       switch (climate_control.preset.value()) { | ||||
|         case CLIMATE_PRESET_NONE: | ||||
|           out_data->quiet_mode = 0; | ||||
|           out_data->fast_mode = 0; | ||||
|           out_data->sleep_mode = 0; | ||||
|           out_data->ten_degree = 0; | ||||
|           break; | ||||
|         case CLIMATE_PRESET_ECO: | ||||
|           // Eco is not supported in Fan only mode | ||||
|           out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; | ||||
|           out_data->fast_mode = 0; | ||||
|           out_data->sleep_mode = 0; | ||||
|           out_data->ten_degree = 0; | ||||
|           break; | ||||
|         case CLIMATE_PRESET_BOOST: | ||||
|           out_data->quiet_mode = 0; | ||||
|           // Boost is not supported in Fan only mode | ||||
|           out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; | ||||
|           out_data->sleep_mode = 0; | ||||
|           out_data->ten_degree = 0; | ||||
|           break; | ||||
|         case CLIMATE_PRESET_AWAY: | ||||
|           out_data->quiet_mode = 0; | ||||
|           out_data->fast_mode = 0; | ||||
|           out_data->sleep_mode = 0; | ||||
|           // 10 degrees allowed only in heat mode | ||||
|           out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0; | ||||
|           break; | ||||
|         case CLIMATE_PRESET_SLEEP: | ||||
|           out_data->quiet_mode = 0; | ||||
|           out_data->fast_mode = 0; | ||||
|           out_data->sleep_mode = 1; | ||||
|           out_data->ten_degree = 0; | ||||
|           break; | ||||
|         default: | ||||
|           ESP_LOGE("Control", "Unsupported preset"); | ||||
|           out_data->quiet_mode = 0; | ||||
|           out_data->fast_mode = 0; | ||||
|           out_data->sleep_mode = 0; | ||||
|           out_data->ten_degree = 0; | ||||
| @@ -638,10 +650,23 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | ||||
|     out_data->horizontal_swing_mode = (uint8_t) this->pending_horizontal_direction_.value(); | ||||
|     this->pending_horizontal_direction_.reset(); | ||||
|   } | ||||
|   out_data->beeper_status = ((!this->beeper_status_) || (!has_hvac_settings)) ? 1 : 0; | ||||
|   { | ||||
|     // Quiet mode | ||||
|     if ((out_data->ac_power == 0) || (out_data->ac_mode == (uint8_t) hon_protocol::ConditioningMode::FAN)) { | ||||
|       // If AC is off or in fan only mode - no quiet mode allowed | ||||
|       out_data->quiet_mode = 0; | ||||
|     } else { | ||||
|       out_data->quiet_mode = this->get_quiet_mode_state() ? 1 : 0; | ||||
|     } | ||||
|     // Clean quiet mode state pending flag | ||||
|     this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01); | ||||
|   } | ||||
|   out_data->beeper_status = ((!this->get_beeper_state()) || (!has_hvac_settings)) ? 1 : 0; | ||||
|   control_out_buffer[4] = 0;  // This byte should be cleared before setting values | ||||
|   out_data->display_status = this->display_status_ ? 1 : 0; | ||||
|   out_data->health_mode = this->health_mode_ ? 1 : 0; | ||||
|   out_data->display_status = this->get_display_state() ? 1 : 0; | ||||
|   this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01); | ||||
|   out_data->health_mode = this->get_health_mode() ? 1 : 0; | ||||
|   this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01); | ||||
|   return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, | ||||
|                                       (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS, | ||||
|                                       control_out_buffer, this->real_control_packet_size_); | ||||
| @@ -765,6 +790,22 @@ void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::stri | ||||
| } | ||||
| #endif  // USE_TEXT_SENSOR | ||||
|  | ||||
| #ifdef USE_SWITCH | ||||
| void HonClimate::set_beeper_switch(switch_::Switch *sw) { | ||||
|   this->beeper_switch_ = sw; | ||||
|   if (this->beeper_switch_ != nullptr) { | ||||
|     this->beeper_switch_->publish_state(this->get_beeper_state()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void HonClimate::set_quiet_mode_switch(switch_::Switch *sw) { | ||||
|   this->quiet_mode_switch_ = sw; | ||||
|   if (this->quiet_mode_switch_ != nullptr) { | ||||
|     this->quiet_mode_switch_->publish_state(this->settings_.quiet_mode_state); | ||||
|   } | ||||
| } | ||||
| #endif  // USE_SWITCH | ||||
|  | ||||
| haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) { | ||||
|   size_t expected_size = | ||||
|       2 + this->status_message_header_size_ + this->real_control_packet_size_ + this->real_sensors_packet_size_; | ||||
| @@ -827,9 +868,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * | ||||
|   { | ||||
|     // Extra modes/presets | ||||
|     optional<ClimatePreset> old_preset = this->preset; | ||||
|     if (packet.control.quiet_mode != 0) { | ||||
|       this->preset = CLIMATE_PRESET_ECO; | ||||
|     } else if (packet.control.fast_mode != 0) { | ||||
|     if (packet.control.fast_mode != 0) { | ||||
|       this->preset = CLIMATE_PRESET_BOOST; | ||||
|     } else if (packet.control.sleep_mode != 0) { | ||||
|       this->preset = CLIMATE_PRESET_SLEEP; | ||||
| @@ -883,28 +922,26 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * | ||||
|     } | ||||
|     should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value()); | ||||
|   } | ||||
|   { | ||||
|   // Display status | ||||
|   // should be before "Climate mode" because it is changing this->mode | ||||
|   if (packet.control.ac_power != 0) { | ||||
|     // if AC is off display status always ON so process it only when AC is on | ||||
|     bool disp_status = packet.control.display_status != 0; | ||||
|       if (disp_status != this->display_status_) { | ||||
|     if (disp_status != this->get_display_state()) { | ||||
|       // Do something only if display status changed | ||||
|       if (this->mode == CLIMATE_MODE_OFF) { | ||||
|         // AC just turned on from remote need to turn off display | ||||
|         this->force_send_control_ = true; | ||||
|         } else { | ||||
|           this->display_status_ = disp_status; | ||||
|       } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) { | ||||
|         this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   } | ||||
|   { | ||||
|   // Health mode | ||||
|     bool old_health_mode = this->health_mode_; | ||||
|     this->health_mode_ = packet.control.health_mode == 1; | ||||
|     should_publish = should_publish || (old_health_mode != this->health_mode_); | ||||
|   if ((((uint8_t) this->health_mode_) & 0b10) == 0) { | ||||
|     bool old_health_mode = this->get_health_mode(); | ||||
|     this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF; | ||||
|     should_publish = should_publish || (old_health_mode != this->get_health_mode()); | ||||
|   } | ||||
|   { | ||||
|     CleaningState new_cleaning; | ||||
| @@ -958,17 +995,36 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * | ||||
|     } | ||||
|     should_publish = should_publish || (old_mode != this->mode); | ||||
|   } | ||||
|   { | ||||
|     // Quiet mode, should be after climate mode | ||||
|     if ((this->mode != CLIMATE_MODE_FAN_ONLY) && (this->mode != CLIMATE_MODE_OFF) && | ||||
|         ((((uint8_t) this->quiet_mode_state_) & 0b10) == 0)) { | ||||
|       // In proper mode and not in pending state | ||||
|       bool new_quiet_mode = packet.control.quiet_mode != 0; | ||||
|       if (new_quiet_mode != this->get_quiet_mode_state()) { | ||||
|         this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF; | ||||
|         this->settings_.quiet_mode_state = new_quiet_mode; | ||||
|         this->hon_rtc_.save(&this->settings_); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   { | ||||
|     // Swing mode | ||||
|     ClimateSwingMode old_swing_mode = this->swing_mode; | ||||
|     if (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO) { | ||||
|       if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) { | ||||
|     const std::set<ClimateSwingMode> &swing_modes = traits_.get_supported_swing_modes(); | ||||
|     bool vertical_swing_supported = swing_modes.find(CLIMATE_SWING_VERTICAL) != swing_modes.end(); | ||||
|     bool horizontal_swing_supported = swing_modes.find(CLIMATE_SWING_HORIZONTAL) != swing_modes.end(); | ||||
|     if (horizontal_swing_supported && | ||||
|         (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO)) { | ||||
|       if (vertical_swing_supported && | ||||
|           (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) { | ||||
|         this->swing_mode = CLIMATE_SWING_BOTH; | ||||
|       } else { | ||||
|         this->swing_mode = CLIMATE_SWING_HORIZONTAL; | ||||
|       } | ||||
|     } else { | ||||
|       if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) { | ||||
|       if (vertical_swing_supported && | ||||
|           (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) { | ||||
|         this->swing_mode = CLIMATE_SWING_VERTICAL; | ||||
|       } else { | ||||
|         this->swing_mode = CLIMATE_SWING_OFF; | ||||
| @@ -985,7 +1041,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * | ||||
|     if (save_settings) { | ||||
|       this->settings_.last_vertiacal_swing = this->current_vertical_swing_.value(); | ||||
|       this->settings_.last_horizontal_swing = this->current_horizontal_swing_.value(); | ||||
|       this->rtc_.save(&this->settings_); | ||||
|       this->hon_rtc_.save(&this->settings_); | ||||
|     } | ||||
|     should_publish = should_publish || (old_swing_mode != this->swing_mode); | ||||
|   } | ||||
| @@ -1017,7 +1073,7 @@ void HonClimate::fill_control_messages_queue_() { | ||||
|         haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, | ||||
|                                      (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + | ||||
|                                          (uint8_t) hon_protocol::DataParameters::BEEPER_STATUS, | ||||
|                                      this->beeper_status_ ? ZERO_BUF : ONE_BUF, 2)); | ||||
|                                      this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2)); | ||||
|   } | ||||
|   // Health mode | ||||
|   { | ||||
| @@ -1025,13 +1081,16 @@ void HonClimate::fill_control_messages_queue_() { | ||||
|         haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, | ||||
|                                      (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + | ||||
|                                          (uint8_t) hon_protocol::DataParameters::HEALTH_MODE, | ||||
|                                      this->health_mode_ ? ONE_BUF : ZERO_BUF, 2)); | ||||
|                                      this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2)); | ||||
|     this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01); | ||||
|   } | ||||
|   // Climate mode | ||||
|   ClimateMode climate_mode = this->mode; | ||||
|   bool new_power = this->mode != CLIMATE_MODE_OFF; | ||||
|   uint8_t fan_mode_buf[] = {0x00, 0xFF}; | ||||
|   uint8_t quiet_mode_buf[] = {0x00, 0xFF}; | ||||
|   if (climate_control.mode.has_value()) { | ||||
|     climate_mode = climate_control.mode.value(); | ||||
|     uint8_t buffer[2] = {0x00, 0x00}; | ||||
|     switch (climate_control.mode.value()) { | ||||
|       case CLIMATE_MODE_OFF: | ||||
| @@ -1076,8 +1135,6 @@ void HonClimate::fill_control_messages_queue_() { | ||||
|                                              (uint8_t) hon_protocol::DataParameters::AC_MODE, | ||||
|                                          buffer, 2)); | ||||
|         fan_mode_buf[1] = this->other_modes_fan_speed_;  // Auto doesn't work in fan only mode | ||||
|         // Disabling eco mode for Fan only | ||||
|         quiet_mode_buf[1] = 0; | ||||
|         break; | ||||
|       case CLIMATE_MODE_COOL: | ||||
|         new_power = true; | ||||
| @@ -1108,30 +1165,20 @@ void HonClimate::fill_control_messages_queue_() { | ||||
|     uint8_t away_mode_buf[] = {0x00, 0xFF}; | ||||
|     if (!new_power) { | ||||
|       // If AC is off - no presets allowed | ||||
|       quiet_mode_buf[1] = 0x00; | ||||
|       fast_mode_buf[1] = 0x00; | ||||
|       away_mode_buf[1] = 0x00; | ||||
|     } else if (climate_control.preset.has_value()) { | ||||
|       switch (climate_control.preset.value()) { | ||||
|         case CLIMATE_PRESET_NONE: | ||||
|           quiet_mode_buf[1] = 0x00; | ||||
|           fast_mode_buf[1] = 0x00; | ||||
|           away_mode_buf[1] = 0x00; | ||||
|           break; | ||||
|         case CLIMATE_PRESET_ECO: | ||||
|           // Eco is not supported in Fan only mode | ||||
|           quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00; | ||||
|           fast_mode_buf[1] = 0x00; | ||||
|           away_mode_buf[1] = 0x00; | ||||
|           break; | ||||
|         case CLIMATE_PRESET_BOOST: | ||||
|           quiet_mode_buf[1] = 0x00; | ||||
|           // Boost is not supported in Fan only mode | ||||
|           fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00; | ||||
|           away_mode_buf[1] = 0x00; | ||||
|           break; | ||||
|         case CLIMATE_PRESET_AWAY: | ||||
|           quiet_mode_buf[1] = 0x00; | ||||
|           fast_mode_buf[1] = 0x00; | ||||
|           away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00; | ||||
|           break; | ||||
| @@ -1140,8 +1187,18 @@ void HonClimate::fill_control_messages_queue_() { | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|     { | ||||
|       // Quiet mode | ||||
|       if (new_power && (climate_mode != CLIMATE_MODE_FAN_ONLY) && this->get_quiet_mode_state()) { | ||||
|         quiet_mode_buf[1] = 0x01; | ||||
|       } else { | ||||
|         quiet_mode_buf[1] = 0x00; | ||||
|       } | ||||
|       // Clean quiet mode state pending flag | ||||
|       this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01); | ||||
|     } | ||||
|     auto presets = this->traits_.get_supported_presets(); | ||||
|     if ((quiet_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_ECO) != presets.end()))) { | ||||
|     if (quiet_mode_buf[1] != 0xFF) { | ||||
|       this->control_messages_queue_.push( | ||||
|           haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, | ||||
|                                        (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + | ||||
|   | ||||
| @@ -10,6 +10,9 @@ | ||||
| #ifdef USE_TEXT_SENSOR | ||||
| #include "esphome/components/text_sensor/text_sensor.h" | ||||
| #endif | ||||
| #ifdef USE_SWITCH | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #endif | ||||
| #include "esphome/core/automation.h" | ||||
| #include "haier_base.h" | ||||
| #include "hon_packet.h" | ||||
| @@ -28,6 +31,8 @@ enum class HonControlMethod { MONITOR_ONLY = 0, SET_GROUP_PARAMETERS, SET_SINGLE | ||||
| struct HonSettings { | ||||
|   hon_protocol::VerticalSwingMode last_vertiacal_swing; | ||||
|   hon_protocol::HorizontalSwingMode last_horizontal_swing; | ||||
|   bool beeper_state; | ||||
|   bool quiet_mode_state; | ||||
| }; | ||||
|  | ||||
| class HonClimate : public HaierClimateBase { | ||||
| @@ -86,6 +91,15 @@ class HonClimate : public HaierClimateBase { | ||||
|  protected: | ||||
|   void update_sub_text_sensor_(SubTextSensorType type, const std::string &value); | ||||
|   text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr}; | ||||
| #endif | ||||
| #ifdef USE_SWITCH | ||||
|  public: | ||||
|   void set_beeper_switch(switch_::Switch *sw); | ||||
|   void set_quiet_mode_switch(switch_::Switch *sw); | ||||
|  | ||||
|  protected: | ||||
|   switch_::Switch *beeper_switch_{nullptr}; | ||||
|   switch_::Switch *quiet_mode_switch_{nullptr}; | ||||
| #endif | ||||
|  public: | ||||
|   HonClimate(); | ||||
| @@ -95,6 +109,8 @@ class HonClimate : public HaierClimateBase { | ||||
|   void dump_config() override; | ||||
|   void set_beeper_state(bool state); | ||||
|   bool get_beeper_state() const; | ||||
|   void set_quiet_mode_state(bool state); | ||||
|   bool get_quiet_mode_state() const; | ||||
|   esphome::optional<hon_protocol::VerticalSwingMode> get_vertical_airflow() const; | ||||
|   void set_vertical_airflow(hon_protocol::VerticalSwingMode direction); | ||||
|   esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const; | ||||
| @@ -153,7 +169,6 @@ class HonClimate : public HaierClimateBase { | ||||
|     bool functions_[5]; | ||||
|   }; | ||||
|  | ||||
|   bool beeper_status_; | ||||
|   CleaningState cleaning_status_; | ||||
|   bool got_valid_outdoor_temp_; | ||||
|   esphome::optional<hon_protocol::VerticalSwingMode> pending_vertical_direction_{}; | ||||
| @@ -175,7 +190,8 @@ class HonClimate : public HaierClimateBase { | ||||
|   esphome::optional<hon_protocol::VerticalSwingMode> current_vertical_swing_{}; | ||||
|   esphome::optional<hon_protocol::HorizontalSwingMode> current_horizontal_swing_{}; | ||||
|   HonSettings settings_; | ||||
|   ESPPreferenceObject rtc_; | ||||
|   ESPPreferenceObject hon_rtc_; | ||||
|   SwitchState quiet_mode_state_{SwitchState::OFF}; | ||||
| }; | ||||
|  | ||||
| class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> { | ||||
|   | ||||
| @@ -376,8 +376,10 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() { | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   out_data->display_status = this->display_status_ ? 0 : 1; | ||||
|   out_data->health_mode = this->health_mode_ ? 1 : 0; | ||||
|   out_data->display_status = this->get_display_state() ? 0 : 1; | ||||
|   this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01); | ||||
|   out_data->health_mode = this->get_health_mode() ? 1 : 0; | ||||
|   this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01); | ||||
|   return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, 0x4D5F, control_out_buffer, | ||||
|                                       sizeof(smartair2_protocol::HaierPacketControl)); | ||||
| } | ||||
| @@ -446,28 +448,26 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin | ||||
|     } | ||||
|     should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value()); | ||||
|   } | ||||
|   { | ||||
|   // Display status | ||||
|   // should be before "Climate mode" because it is changing this->mode | ||||
|   if (packet.control.ac_power != 0) { | ||||
|     // if AC is off display status always ON so process it only when AC is on | ||||
|     bool disp_status = packet.control.display_status == 0; | ||||
|       if (disp_status != this->display_status_) { | ||||
|     if (disp_status != this->get_display_state()) { | ||||
|       // Do something only if display status changed | ||||
|       if (this->mode == CLIMATE_MODE_OFF) { | ||||
|         // AC just turned on from remote need to turn off display | ||||
|         this->force_send_control_ = true; | ||||
|         } else { | ||||
|           this->display_status_ = disp_status; | ||||
|       } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) { | ||||
|         this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   } | ||||
|   { | ||||
|   // Health mode | ||||
|     bool old_health_mode = this->health_mode_; | ||||
|     this->health_mode_ = packet.control.health_mode == 1; | ||||
|     should_publish = should_publish || (old_health_mode != this->health_mode_); | ||||
|   if ((((uint8_t) this->health_mode_) & 0b10) == 0) { | ||||
|     bool old_health_mode = this->get_health_mode(); | ||||
|     this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF; | ||||
|     should_publish = should_publish || (old_health_mode != this->get_health_mode()); | ||||
|   } | ||||
|   { | ||||
|     // Climate mode | ||||
|   | ||||
							
								
								
									
										91
									
								
								esphome/components/haier/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								esphome/components/haier/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| import esphome.codegen as cg | ||||
| import esphome.config_validation as cv | ||||
| import esphome.final_validate as fv | ||||
| from esphome.components import switch | ||||
| from esphome.const import ( | ||||
|     CONF_BEEPER, | ||||
|     CONF_DISPLAY, | ||||
|     ENTITY_CATEGORY_CONFIG, | ||||
| ) | ||||
| from ..climate import ( | ||||
|     CONF_HAIER_ID, | ||||
|     CONF_PROTOCOL, | ||||
|     HaierClimateBase, | ||||
|     haier_ns, | ||||
|     PROTOCOL_HON, | ||||
| ) | ||||
|  | ||||
| CODEOWNERS = ["@paveldn"] | ||||
| BeeperSwitch = haier_ns.class_("BeeperSwitch", switch.Switch) | ||||
| HealthModeSwitch = haier_ns.class_("HealthModeSwitch", switch.Switch) | ||||
| DisplaySwitch = haier_ns.class_("DisplaySwitch", switch.Switch) | ||||
| QuietModeSwitch = haier_ns.class_("QuietModeSwitch", switch.Switch) | ||||
|  | ||||
| # Haier switches | ||||
| CONF_HEALTH_MODE = "health_mode" | ||||
| CONF_QUIET_MODE = "quiet_mode" | ||||
|  | ||||
| # Additional icons | ||||
| ICON_LEAF = "mdi:leaf" | ||||
| ICON_LED_ON = "mdi:led-on" | ||||
| ICON_VOLUME_HIGH = "mdi:volume-high" | ||||
| ICON_VOLUME_OFF = "mdi:volume-off" | ||||
|  | ||||
| CONFIG_SCHEMA = cv.Schema( | ||||
|     { | ||||
|         cv.GenerateID(CONF_HAIER_ID): cv.use_id(HaierClimateBase), | ||||
|         cv.Optional(CONF_DISPLAY): switch.switch_schema( | ||||
|             DisplaySwitch, | ||||
|             icon=ICON_LED_ON, | ||||
|             entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|             default_restore_mode="DISABLED", | ||||
|         ), | ||||
|         cv.Optional(CONF_HEALTH_MODE): switch.switch_schema( | ||||
|             HealthModeSwitch, | ||||
|             icon=ICON_LEAF, | ||||
|             default_restore_mode="DISABLED", | ||||
|         ), | ||||
|         # Beeper switch is only supported for HonClimate | ||||
|         cv.Optional(CONF_BEEPER): switch.switch_schema( | ||||
|             BeeperSwitch, | ||||
|             icon=ICON_VOLUME_HIGH, | ||||
|             entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|             default_restore_mode="DISABLED", | ||||
|         ), | ||||
|         # Quiet mode is only supported for HonClimate | ||||
|         cv.Optional(CONF_QUIET_MODE): switch.switch_schema( | ||||
|             QuietModeSwitch, | ||||
|             icon=ICON_VOLUME_OFF, | ||||
|             entity_category=ENTITY_CATEGORY_CONFIG, | ||||
|             default_restore_mode="DISABLED", | ||||
|         ), | ||||
|     } | ||||
| ) | ||||
|  | ||||
|  | ||||
| def _final_validate(config): | ||||
|     full_config = fv.full_config.get() | ||||
|     for switch_type in [CONF_BEEPER, CONF_QUIET_MODE]: | ||||
|         # Check switches that are only supported for HonClimate | ||||
|         if config.get(switch_type): | ||||
|             climate_path = full_config.get_path_for_id(config[CONF_HAIER_ID])[:-1] | ||||
|             climate_conf = full_config.get_config_for_path(climate_path) | ||||
|             protocol_type = climate_conf.get(CONF_PROTOCOL) | ||||
|             if protocol_type.casefold() != PROTOCOL_HON.casefold(): | ||||
|                 raise cv.Invalid( | ||||
|                     f"{switch_type} switch is only supported for hon climate" | ||||
|                 ) | ||||
|     return config | ||||
|  | ||||
|  | ||||
| FINAL_VALIDATE_SCHEMA = _final_validate | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     parent = await cg.get_variable(config[CONF_HAIER_ID]) | ||||
|  | ||||
|     for switch_type in [CONF_DISPLAY, CONF_HEALTH_MODE, CONF_BEEPER, CONF_QUIET_MODE]: | ||||
|         if conf := config.get(switch_type): | ||||
|             sw_var = await switch.new_switch(conf) | ||||
|             await cg.register_parented(sw_var, parent) | ||||
|             cg.add(getattr(parent, f"set_{switch_type}_switch")(sw_var)) | ||||
							
								
								
									
										14
									
								
								esphome/components/haier/switch/beeper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								esphome/components/haier/switch/beeper.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| #include "beeper.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| void BeeperSwitch::write_state(bool state) { | ||||
|   if (this->parent_->get_beeper_state() != state) { | ||||
|     this->parent_->set_beeper_state(state); | ||||
|   } | ||||
|   this->publish_state(state); | ||||
| } | ||||
|  | ||||
| }  // namespace haier | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/haier/switch/beeper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/haier/switch/beeper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #include "../hon_climate.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| class BeeperSwitch : public switch_::Switch, public Parented<HonClimate> { | ||||
|  public: | ||||
|   BeeperSwitch() = default; | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace haier | ||||
| }  // namespace esphome | ||||
							
								
								
									
										14
									
								
								esphome/components/haier/switch/display.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								esphome/components/haier/switch/display.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| #include "display.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| void DisplaySwitch::write_state(bool state) { | ||||
|   if (this->parent_->get_display_state() != state) { | ||||
|     this->parent_->set_display_state(state); | ||||
|   } | ||||
|   this->publish_state(state); | ||||
| } | ||||
|  | ||||
| }  // namespace haier | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/haier/switch/display.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/haier/switch/display.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #include "../haier_base.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| class DisplaySwitch : public switch_::Switch, public Parented<HaierClimateBase> { | ||||
|  public: | ||||
|   DisplaySwitch() = default; | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace haier | ||||
| }  // namespace esphome | ||||
							
								
								
									
										14
									
								
								esphome/components/haier/switch/health_mode.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								esphome/components/haier/switch/health_mode.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| #include "health_mode.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| void HealthModeSwitch::write_state(bool state) { | ||||
|   if (this->parent_->get_health_mode() != state) { | ||||
|     this->parent_->set_health_mode(state); | ||||
|   } | ||||
|   this->publish_state(state); | ||||
| } | ||||
|  | ||||
| }  // namespace haier | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/haier/switch/health_mode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/haier/switch/health_mode.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #include "../haier_base.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| class HealthModeSwitch : public switch_::Switch, public Parented<HaierClimateBase> { | ||||
|  public: | ||||
|   HealthModeSwitch() = default; | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace haier | ||||
| }  // namespace esphome | ||||
							
								
								
									
										14
									
								
								esphome/components/haier/switch/quiet_mode.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								esphome/components/haier/switch/quiet_mode.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| #include "quiet_mode.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| void QuietModeSwitch::write_state(bool state) { | ||||
|   if (this->parent_->get_quiet_mode_state() != state) { | ||||
|     this->parent_->set_quiet_mode_state(state); | ||||
|   } | ||||
|   this->publish_state(state); | ||||
| } | ||||
|  | ||||
| }  // namespace haier | ||||
| }  // namespace esphome | ||||
							
								
								
									
										18
									
								
								esphome/components/haier/switch/quiet_mode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/haier/switch/quiet_mode.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/components/switch/switch.h" | ||||
| #include "../hon_climate.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace haier { | ||||
|  | ||||
| class QuietModeSwitch : public switch_::Switch, public Parented<HonClimate> { | ||||
|  public: | ||||
|   QuietModeSwitch() = default; | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace haier | ||||
| }  // namespace esphome | ||||
| @@ -16,7 +16,6 @@ climate: | ||||
|     name: Haier AC | ||||
|     wifi_signal: true | ||||
|     answer_timeout: 200ms | ||||
|     beeper: true | ||||
|     visual: | ||||
|       min_temperature: 16 °C | ||||
|       max_temperature: 30 °C | ||||
| @@ -38,7 +37,6 @@ climate: | ||||
|     supported_presets: | ||||
|       - AWAY | ||||
|       - BOOST | ||||
|       - ECO | ||||
|       - SLEEP | ||||
|     on_alarm_start: | ||||
|       then: | ||||
| @@ -112,3 +110,15 @@ text_sensor: | ||||
|       name: Haier cleaning status | ||||
|     protocol_version: | ||||
|       name: Haier protocol version | ||||
|  | ||||
| switch: | ||||
|   - platform: haier | ||||
|     haier_id: haier_ac | ||||
|     beeper: | ||||
|       name: Haier beeper | ||||
|     display: | ||||
|       name: Haier display | ||||
|     health_mode: | ||||
|       name: Haier health mode | ||||
|     quiet_mode: | ||||
|       name: Haier quiet mode | ||||
|   | ||||
		Reference in New Issue
	
	Block a user