mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	Added alarm processing for Haier component (hOn protocol) (#5965)
This commit is contained in:
		| @@ -18,6 +18,7 @@ from esphome.const import ( | |||||||
|     CONF_SUPPORTED_SWING_MODES, |     CONF_SUPPORTED_SWING_MODES, | ||||||
|     CONF_TARGET_TEMPERATURE, |     CONF_TARGET_TEMPERATURE, | ||||||
|     CONF_TEMPERATURE_STEP, |     CONF_TEMPERATURE_STEP, | ||||||
|  |     CONF_TRIGGER_ID, | ||||||
|     CONF_VISUAL, |     CONF_VISUAL, | ||||||
|     CONF_WIFI, |     CONF_WIFI, | ||||||
|     DEVICE_CLASS_TEMPERATURE, |     DEVICE_CLASS_TEMPERATURE, | ||||||
| @@ -49,6 +50,8 @@ CONF_CONTROL_METHOD = "control_method" | |||||||
| CONF_CONTROL_PACKET_SIZE = "control_packet_size" | CONF_CONTROL_PACKET_SIZE = "control_packet_size" | ||||||
| CONF_DISPLAY = "display" | CONF_DISPLAY = "display" | ||||||
| CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow" | CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow" | ||||||
|  | CONF_ON_ALARM_START = "on_alarm_start" | ||||||
|  | CONF_ON_ALARM_END = "on_alarm_end" | ||||||
| CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" | CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" | ||||||
| CONF_VERTICAL_AIRFLOW = "vertical_airflow" | CONF_VERTICAL_AIRFLOW = "vertical_airflow" | ||||||
| CONF_WIFI_SIGNAL = "wifi_signal" | CONF_WIFI_SIGNAL = "wifi_signal" | ||||||
| @@ -85,8 +88,8 @@ AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS = { | |||||||
| } | } | ||||||
|  |  | ||||||
| SUPPORTED_SWING_MODES_OPTIONS = { | SUPPORTED_SWING_MODES_OPTIONS = { | ||||||
|     "OFF": ClimateSwingMode.CLIMATE_SWING_OFF,  # always available |     "OFF": ClimateSwingMode.CLIMATE_SWING_OFF, | ||||||
|     "VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL,  # always available |     "VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL, | ||||||
|     "HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL, |     "HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL, | ||||||
|     "BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH, |     "BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH, | ||||||
| } | } | ||||||
| @@ -101,13 +104,15 @@ SUPPORTED_CLIMATE_MODES_OPTIONS = { | |||||||
| } | } | ||||||
|  |  | ||||||
| SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = { | SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = { | ||||||
|  |     "AWAY": ClimatePreset.CLIMATE_PRESET_AWAY, | ||||||
|     "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, |     "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, | ||||||
|     "COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT, |     "COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT, | ||||||
| } | } | ||||||
|  |  | ||||||
| SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = { | SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = { | ||||||
|     "ECO": ClimatePreset.CLIMATE_PRESET_ECO, |     "AWAY": ClimatePreset.CLIMATE_PRESET_AWAY, | ||||||
|     "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, |     "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, | ||||||
|  |     "ECO": ClimatePreset.CLIMATE_PRESET_ECO, | ||||||
|     "SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP, |     "SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP, | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -118,6 +123,16 @@ SUPPORTED_HON_CONTROL_METHODS = { | |||||||
|     "SET_SINGLE_PARAMETER": HonControlMethod.SET_SINGLE_PARAMETER, |     "SET_SINGLE_PARAMETER": HonControlMethod.SET_SINGLE_PARAMETER, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | HaierAlarmStartTrigger = haier_ns.class_( | ||||||
|  |     "HaierAlarmStartTrigger", | ||||||
|  |     automation.Trigger.template(cg.uint8, cg.const_char_ptr), | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | HaierAlarmEndTrigger = haier_ns.class_( | ||||||
|  |     "HaierAlarmEndTrigger", | ||||||
|  |     automation.Trigger.template(cg.uint8, cg.const_char_ptr), | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def validate_visual(config): | def validate_visual(config): | ||||||
|     if CONF_VISUAL in config: |     if CONF_VISUAL in config: | ||||||
| @@ -200,9 +215,7 @@ CONFIG_SCHEMA = cv.All( | |||||||
|                     ): cv.boolean, |                     ): cv.boolean, | ||||||
|                     cv.Optional( |                     cv.Optional( | ||||||
|                         CONF_SUPPORTED_PRESETS, |                         CONF_SUPPORTED_PRESETS, | ||||||
|                         default=list( |                         default=list(["BOOST", "COMFORT"]),  # No AWAY by default | ||||||
|                             SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS.keys() |  | ||||||
|                         ), |  | ||||||
|                     ): cv.ensure_list( |                     ): cv.ensure_list( | ||||||
|                         cv.enum(SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS, upper=True) |                         cv.enum(SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS, upper=True) | ||||||
|                     ), |                     ), | ||||||
| @@ -222,7 +235,7 @@ CONFIG_SCHEMA = cv.All( | |||||||
|                     ): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50), |                     ): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50), | ||||||
|                     cv.Optional( |                     cv.Optional( | ||||||
|                         CONF_SUPPORTED_PRESETS, |                         CONF_SUPPORTED_PRESETS, | ||||||
|                         default=list(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS.keys()), |                         default=list(["BOOST", "ECO", "SLEEP"]),  # No AWAY by default | ||||||
|                     ): cv.ensure_list( |                     ): cv.ensure_list( | ||||||
|                         cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True) |                         cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True) | ||||||
|                     ), |                     ), | ||||||
| @@ -233,6 +246,20 @@ CONFIG_SCHEMA = cv.All( | |||||||
|                         device_class=DEVICE_CLASS_TEMPERATURE, |                         device_class=DEVICE_CLASS_TEMPERATURE, | ||||||
|                         state_class=STATE_CLASS_MEASUREMENT, |                         state_class=STATE_CLASS_MEASUREMENT, | ||||||
|                     ), |                     ), | ||||||
|  |                     cv.Optional(CONF_ON_ALARM_START): automation.validate_automation( | ||||||
|  |                         { | ||||||
|  |                             cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||||
|  |                                 HaierAlarmStartTrigger | ||||||
|  |                             ), | ||||||
|  |                         } | ||||||
|  |                     ), | ||||||
|  |                     cv.Optional(CONF_ON_ALARM_END): automation.validate_automation( | ||||||
|  |                         { | ||||||
|  |                             cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( | ||||||
|  |                                 HaierAlarmEndTrigger | ||||||
|  |                             ), | ||||||
|  |                         } | ||||||
|  |                     ), | ||||||
|                 } |                 } | ||||||
|             ), |             ), | ||||||
|         }, |         }, | ||||||
| @@ -457,5 +484,15 @@ async def to_code(config): | |||||||
|                 config[CONF_CONTROL_PACKET_SIZE] - PROTOCOL_CONTROL_PACKET_SIZE |                 config[CONF_CONTROL_PACKET_SIZE] - PROTOCOL_CONTROL_PACKET_SIZE | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|  |     for conf in config.get(CONF_ON_ALARM_START, []): | ||||||
|  |         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||||
|  |         await automation.build_automation( | ||||||
|  |             trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf | ||||||
|  |         ) | ||||||
|  |     for conf in config.get(CONF_ON_ALARM_END, []): | ||||||
|  |         trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) | ||||||
|  |         await automation.build_automation( | ||||||
|  |             trigger, [(cg.uint8, "code"), (cg.const_char_ptr, "message")], conf | ||||||
|  |         ) | ||||||
|     # https://github.com/paveldn/HaierProtocol |     # https://github.com/paveldn/HaierProtocol | ||||||
|     cg.add_library("pavlodn/HaierProtocol", "0.9.24") |     cg.add_library("pavlodn/HaierProtocol", "0.9.24") | ||||||
|   | |||||||
| @@ -25,13 +25,14 @@ const char *HaierClimateBase::phase_to_string_(ProtocolPhases phase) { | |||||||
|       "SENDING_INIT_1", |       "SENDING_INIT_1", | ||||||
|       "SENDING_INIT_2", |       "SENDING_INIT_2", | ||||||
|       "SENDING_FIRST_STATUS_REQUEST", |       "SENDING_FIRST_STATUS_REQUEST", | ||||||
|       "SENDING_ALARM_STATUS_REQUEST", |       "SENDING_FIRST_ALARM_STATUS_REQUEST", | ||||||
|       "IDLE", |       "IDLE", | ||||||
|       "SENDING_STATUS_REQUEST", |       "SENDING_STATUS_REQUEST", | ||||||
|       "SENDING_UPDATE_SIGNAL_REQUEST", |       "SENDING_UPDATE_SIGNAL_REQUEST", | ||||||
|       "SENDING_SIGNAL_LEVEL", |       "SENDING_SIGNAL_LEVEL", | ||||||
|       "SENDING_CONTROL", |       "SENDING_CONTROL", | ||||||
|       "SENDING_ACTION_COMMAND", |       "SENDING_ACTION_COMMAND", | ||||||
|  |       "SENDING_ALARM_STATUS_REQUEST", | ||||||
|       "UNKNOWN"  // Should be the last! |       "UNKNOWN"  // Should be the last! | ||||||
|   }; |   }; | ||||||
|   static_assert( |   static_assert( | ||||||
|   | |||||||
| @@ -64,7 +64,7 @@ class HaierClimateBase : public esphome::Component, | |||||||
|     SENDING_INIT_1 = 0, |     SENDING_INIT_1 = 0, | ||||||
|     SENDING_INIT_2, |     SENDING_INIT_2, | ||||||
|     SENDING_FIRST_STATUS_REQUEST, |     SENDING_FIRST_STATUS_REQUEST, | ||||||
|     SENDING_ALARM_STATUS_REQUEST, |     SENDING_FIRST_ALARM_STATUS_REQUEST, | ||||||
|     // FUNCTIONAL STATE |     // FUNCTIONAL STATE | ||||||
|     IDLE, |     IDLE, | ||||||
|     SENDING_STATUS_REQUEST, |     SENDING_STATUS_REQUEST, | ||||||
| @@ -72,6 +72,7 @@ class HaierClimateBase : public esphome::Component, | |||||||
|     SENDING_SIGNAL_LEVEL, |     SENDING_SIGNAL_LEVEL, | ||||||
|     SENDING_CONTROL, |     SENDING_CONTROL, | ||||||
|     SENDING_ACTION_COMMAND, |     SENDING_ACTION_COMMAND, | ||||||
|  |     SENDING_ALARM_STATUS_REQUEST, | ||||||
|     NUM_PROTOCOL_PHASES |     NUM_PROTOCOL_PHASES | ||||||
|   }; |   }; | ||||||
|   const char *phase_to_string_(ProtocolPhases phase); |   const char *phase_to_string_(ProtocolPhases phase); | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ constexpr size_t SIGNAL_LEVEL_UPDATE_INTERVAL_MS = 10000; | |||||||
| constexpr int PROTOCOL_OUTDOOR_TEMPERATURE_OFFSET = -64; | constexpr int PROTOCOL_OUTDOOR_TEMPERATURE_OFFSET = -64; | ||||||
| constexpr uint8_t CONTROL_MESSAGE_RETRIES = 5; | constexpr uint8_t CONTROL_MESSAGE_RETRIES = 5; | ||||||
| constexpr std::chrono::milliseconds CONTROL_MESSAGE_RETRIES_INTERVAL = std::chrono::milliseconds(500); | constexpr std::chrono::milliseconds CONTROL_MESSAGE_RETRIES_INTERVAL = std::chrono::milliseconds(500); | ||||||
|  | constexpr size_t ALARM_STATUS_REQUEST_INTERVAL_MS = 600000; | ||||||
|  |  | ||||||
| hon_protocol::VerticalSwingMode get_vertical_swing_mode(AirflowVerticalDirection direction) { | hon_protocol::VerticalSwingMode get_vertical_swing_mode(AirflowVerticalDirection direction) { | ||||||
|   switch (direction) { |   switch (direction) { | ||||||
| @@ -110,6 +111,14 @@ void HonClimate::start_steri_cleaning() { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void HonClimate::add_alarm_start_callback(std::function<void(uint8_t, const char *)> &&callback) { | ||||||
|  |   this->alarm_start_callback_.add(std::move(callback)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void HonClimate::add_alarm_end_callback(std::function<void(uint8_t, const char *)> &&callback) { | ||||||
|  |   this->alarm_end_callback_.add(std::move(callback)); | ||||||
|  | } | ||||||
|  |  | ||||||
| haier_protocol::HandlerError HonClimate::get_device_version_answer_handler_(haier_protocol::FrameType request_type, | haier_protocol::HandlerError HonClimate::get_device_version_answer_handler_(haier_protocol::FrameType request_type, | ||||||
|                                                                             haier_protocol::FrameType message_type, |                                                                             haier_protocol::FrameType message_type, | ||||||
|                                                                             const uint8_t *data, size_t data_size) { |                                                                             const uint8_t *data, size_t data_size) { | ||||||
| @@ -194,7 +203,7 @@ haier_protocol::HandlerError HonClimate::status_handler_(haier_protocol::FrameTy | |||||||
|       switch (this->protocol_phase_) { |       switch (this->protocol_phase_) { | ||||||
|         case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST: |         case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST: | ||||||
|           ESP_LOGI(TAG, "First HVAC status received"); |           ESP_LOGI(TAG, "First HVAC status received"); | ||||||
|           this->set_phase(ProtocolPhases::SENDING_ALARM_STATUS_REQUEST); |           this->set_phase(ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST); | ||||||
|           break; |           break; | ||||||
|         case ProtocolPhases::SENDING_ACTION_COMMAND: |         case ProtocolPhases::SENDING_ACTION_COMMAND: | ||||||
|           // Do nothing, phase will be changed in process_phase |           // Do nothing, phase will be changed in process_phase | ||||||
| @@ -251,12 +260,15 @@ haier_protocol::HandlerError HonClimate::get_alarm_status_answer_handler_(haier_ | |||||||
|       this->set_phase(ProtocolPhases::IDLE); |       this->set_phase(ProtocolPhases::IDLE); | ||||||
|       return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; |       return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE; | ||||||
|     } |     } | ||||||
|     if (this->protocol_phase_ != ProtocolPhases::SENDING_ALARM_STATUS_REQUEST) { |     if ((this->protocol_phase_ != ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST) && | ||||||
|  |         (this->protocol_phase_ != ProtocolPhases::SENDING_ALARM_STATUS_REQUEST)) { | ||||||
|       // Don't expect this answer now |       // Don't expect this answer now | ||||||
|       this->set_phase(ProtocolPhases::IDLE); |       this->set_phase(ProtocolPhases::IDLE); | ||||||
|       return haier_protocol::HandlerError::UNEXPECTED_MESSAGE; |       return haier_protocol::HandlerError::UNEXPECTED_MESSAGE; | ||||||
|     } |     } | ||||||
|     memcpy(this->active_alarms_, data + 2, 8); |     if (data_size < sizeof(active_alarms_) + 2) | ||||||
|  |       return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; | ||||||
|  |     this->process_alarm_message_(data, data_size, this->protocol_phase_ >= ProtocolPhases::IDLE); | ||||||
|     this->set_phase(ProtocolPhases::IDLE); |     this->set_phase(ProtocolPhases::IDLE); | ||||||
|     return haier_protocol::HandlerError::HANDLER_OK; |     return haier_protocol::HandlerError::HANDLER_OK; | ||||||
|   } else { |   } else { | ||||||
| @@ -265,6 +277,19 @@ haier_protocol::HandlerError HonClimate::get_alarm_status_answer_handler_(haier_ | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | haier_protocol::HandlerError HonClimate::alarm_status_message_handler_(haier_protocol::FrameType type, | ||||||
|  |                                                                        const uint8_t *buffer, size_t size) { | ||||||
|  |   haier_protocol::HandlerError result = haier_protocol::HandlerError::HANDLER_OK; | ||||||
|  |   if (size < sizeof(this->active_alarms_) + 2) { | ||||||
|  |     // Log error but confirm anyway to avoid to many messages | ||||||
|  |     result = haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; | ||||||
|  |   } | ||||||
|  |   this->process_alarm_message_(buffer, size, true); | ||||||
|  |   this->haier_protocol_.send_answer(haier_protocol::HaierMessage(haier_protocol::FrameType::CONFIRM)); | ||||||
|  |   this->last_alarm_request_ = std::chrono::steady_clock::now(); | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  |  | ||||||
| void HonClimate::set_handlers() { | void HonClimate::set_handlers() { | ||||||
|   // Set handlers |   // Set handlers | ||||||
|   this->haier_protocol_.set_answer_handler( |   this->haier_protocol_.set_answer_handler( | ||||||
| @@ -291,6 +316,10 @@ void HonClimate::set_handlers() { | |||||||
|       haier_protocol::FrameType::REPORT_NETWORK_STATUS, |       haier_protocol::FrameType::REPORT_NETWORK_STATUS, | ||||||
|       std::bind(&HonClimate::report_network_status_answer_handler_, this, std::placeholders::_1, std::placeholders::_2, |       std::bind(&HonClimate::report_network_status_answer_handler_, this, std::placeholders::_1, std::placeholders::_2, | ||||||
|                 std::placeholders::_3, std::placeholders::_4)); |                 std::placeholders::_3, std::placeholders::_4)); | ||||||
|  |   this->haier_protocol_.set_message_handler( | ||||||
|  |       haier_protocol::FrameType::ALARM_STATUS, | ||||||
|  |       std::bind(&HonClimate::alarm_status_message_handler_, this, std::placeholders::_1, std::placeholders::_2, | ||||||
|  |                 std::placeholders::_3)); | ||||||
| } | } | ||||||
|  |  | ||||||
| void HonClimate::dump_config() { | void HonClimate::dump_config() { | ||||||
| @@ -363,10 +392,12 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | |||||||
|       this->set_phase(ProtocolPhases::IDLE); |       this->set_phase(ProtocolPhases::IDLE); | ||||||
|       break; |       break; | ||||||
| #endif | #endif | ||||||
|  |     case ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST: | ||||||
|     case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: |     case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: | ||||||
|       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { |       if (this->can_send_message() && this->is_message_interval_exceeded_(now)) { | ||||||
|         static const haier_protocol::HaierMessage ALARM_STATUS_REQUEST(haier_protocol::FrameType::GET_ALARM_STATUS); |         static const haier_protocol::HaierMessage ALARM_STATUS_REQUEST(haier_protocol::FrameType::GET_ALARM_STATUS); | ||||||
|         this->send_message_(ALARM_STATUS_REQUEST, this->use_crc_); |         this->send_message_(ALARM_STATUS_REQUEST, this->use_crc_); | ||||||
|  |         this->last_alarm_request_ = now; | ||||||
|       } |       } | ||||||
|       break; |       break; | ||||||
|     case ProtocolPhases::SENDING_CONTROL: |     case ProtocolPhases::SENDING_CONTROL: | ||||||
| @@ -417,12 +448,16 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) { | |||||||
|       if (this->forced_request_status_ || this->is_status_request_interval_exceeded_(now)) { |       if (this->forced_request_status_ || this->is_status_request_interval_exceeded_(now)) { | ||||||
|         this->set_phase(ProtocolPhases::SENDING_STATUS_REQUEST); |         this->set_phase(ProtocolPhases::SENDING_STATUS_REQUEST); | ||||||
|         this->forced_request_status_ = false; |         this->forced_request_status_ = false; | ||||||
|  |       } else if (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_alarm_request_).count() > | ||||||
|  |                  ALARM_STATUS_REQUEST_INTERVAL_MS) { | ||||||
|  |         this->set_phase(ProtocolPhases::SENDING_ALARM_STATUS_REQUEST); | ||||||
|       } |       } | ||||||
| #ifdef USE_WIFI | #ifdef USE_WIFI | ||||||
|       else if (this->send_wifi_signal_ && |       else if (this->send_wifi_signal_ && | ||||||
|                (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_signal_request_).count() > |                (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_signal_request_).count() > | ||||||
|                 SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) |                 SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) { | ||||||
|         this->set_phase(ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST); |         this->set_phase(ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST); | ||||||
|  |       } | ||||||
| #endif | #endif | ||||||
|     } break; |     } break; | ||||||
|     default: |     default: | ||||||
| @@ -452,6 +487,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | |||||||
|   uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)]; |   uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)]; | ||||||
|   memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl)); |   memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl)); | ||||||
|   hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer; |   hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer; | ||||||
|  |   control_out_buffer[4] = 0;  // This byte should be cleared before setting values | ||||||
|   bool has_hvac_settings = false; |   bool has_hvac_settings = false; | ||||||
|   if (this->current_hvac_settings_.valid) { |   if (this->current_hvac_settings_.valid) { | ||||||
|     has_hvac_settings = true; |     has_hvac_settings = true; | ||||||
| @@ -552,31 +588,41 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | |||||||
|           out_data->quiet_mode = 0; |           out_data->quiet_mode = 0; | ||||||
|           out_data->fast_mode = 0; |           out_data->fast_mode = 0; | ||||||
|           out_data->sleep_mode = 0; |           out_data->sleep_mode = 0; | ||||||
|  |           out_data->ten_degree = 0; | ||||||
|           break; |           break; | ||||||
|         case CLIMATE_PRESET_ECO: |         case CLIMATE_PRESET_ECO: | ||||||
|           // Eco is not supported in Fan only mode |           // Eco is not supported in Fan only mode | ||||||
|           out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; |           out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; | ||||||
|           out_data->fast_mode = 0; |           out_data->fast_mode = 0; | ||||||
|           out_data->sleep_mode = 0; |           out_data->sleep_mode = 0; | ||||||
|  |           out_data->ten_degree = 0; | ||||||
|           break; |           break; | ||||||
|         case CLIMATE_PRESET_BOOST: |         case CLIMATE_PRESET_BOOST: | ||||||
|           out_data->quiet_mode = 0; |           out_data->quiet_mode = 0; | ||||||
|           // Boost is not supported in Fan only mode |           // Boost is not supported in Fan only mode | ||||||
|           out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; |           out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; | ||||||
|           out_data->sleep_mode = 0; |           out_data->sleep_mode = 0; | ||||||
|  |           out_data->ten_degree = 0; | ||||||
|           break; |           break; | ||||||
|         case CLIMATE_PRESET_AWAY: |         case CLIMATE_PRESET_AWAY: | ||||||
|           out_data->quiet_mode = 0; |           out_data->quiet_mode = 0; | ||||||
|           out_data->fast_mode = 0; |           out_data->fast_mode = 0; | ||||||
|           out_data->sleep_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; |           break; | ||||||
|         case CLIMATE_PRESET_SLEEP: |         case CLIMATE_PRESET_SLEEP: | ||||||
|           out_data->quiet_mode = 0; |           out_data->quiet_mode = 0; | ||||||
|           out_data->fast_mode = 0; |           out_data->fast_mode = 0; | ||||||
|           out_data->sleep_mode = 1; |           out_data->sleep_mode = 1; | ||||||
|  |           out_data->ten_degree = 0; | ||||||
|           break; |           break; | ||||||
|         default: |         default: | ||||||
|           ESP_LOGE("Control", "Unsupported preset"); |           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; | ||||||
|           break; |           break; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| @@ -595,6 +641,50 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { | |||||||
|                                       control_out_buffer, sizeof(hon_protocol::HaierPacketControl)); |                                       control_out_buffer, sizeof(hon_protocol::HaierPacketControl)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void HonClimate::process_alarm_message_(const uint8_t *packet, uint8_t size, bool check_new) { | ||||||
|  |   constexpr size_t active_alarms_size = sizeof(this->active_alarms_); | ||||||
|  |   if (size >= active_alarms_size + 2) { | ||||||
|  |     if (check_new) { | ||||||
|  |       size_t alarm_code = 0; | ||||||
|  |       for (int i = active_alarms_size - 1; i >= 0; i--) { | ||||||
|  |         if (packet[2 + i] != active_alarms_[i]) { | ||||||
|  |           uint8_t alarm_bit = 1; | ||||||
|  |           for (int b = 0; b < 8; b++) { | ||||||
|  |             if ((packet[2 + i] & alarm_bit) != (this->active_alarms_[i] & alarm_bit)) { | ||||||
|  |               bool alarm_status = (packet[2 + i] & alarm_bit) != 0; | ||||||
|  |               int log_level = alarm_status ? ESPHOME_LOG_LEVEL_WARN : ESPHOME_LOG_LEVEL_INFO; | ||||||
|  |               const char *alarm_message = alarm_code < esphome::haier::hon_protocol::HON_ALARM_COUNT | ||||||
|  |                                               ? esphome::haier::hon_protocol::HON_ALARM_MESSAGES[alarm_code].c_str() | ||||||
|  |                                               : "Unknown"; | ||||||
|  |               esp_log_printf_(log_level, TAG, __LINE__, "Alarm %s (%d): %s", alarm_status ? "activated" : "deactivated", | ||||||
|  |                               alarm_code, alarm_message); | ||||||
|  |               if (alarm_status) { | ||||||
|  |                 this->alarm_start_callback_.call(alarm_code, alarm_message); | ||||||
|  |                 this->active_alarm_count_ += 1.0f; | ||||||
|  |               } else { | ||||||
|  |                 this->alarm_end_callback_.call(alarm_code, alarm_message); | ||||||
|  |                 this->active_alarm_count_ -= 1.0f; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             alarm_bit <<= 1; | ||||||
|  |             alarm_code++; | ||||||
|  |           } | ||||||
|  |           active_alarms_[i] = packet[2 + i]; | ||||||
|  |         } else | ||||||
|  |           alarm_code += 8; | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       float alarm_count = 0.0f; | ||||||
|  |       static uint8_t nibble_bits_count[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; | ||||||
|  |       for (size_t i = 0; i < sizeof(this->active_alarms_); i++) { | ||||||
|  |         alarm_count += (float) (nibble_bits_count[packet[2 + i] & 0x0F] + nibble_bits_count[packet[2 + i] >> 4]); | ||||||
|  |       } | ||||||
|  |       this->active_alarm_count_ = alarm_count; | ||||||
|  |       memcpy(this->active_alarms_, packet + 2, sizeof(this->active_alarms_)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) { | haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) { | ||||||
|   if (size < hon_protocol::HAIER_STATUS_FRAME_SIZE + this->extra_control_packet_bytes_) |   if (size < hon_protocol::HAIER_STATUS_FRAME_SIZE + this->extra_control_packet_bytes_) | ||||||
|     return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; |     return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE; | ||||||
| @@ -626,6 +716,8 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * | |||||||
|       this->preset = CLIMATE_PRESET_BOOST; |       this->preset = CLIMATE_PRESET_BOOST; | ||||||
|     } else if (packet.control.sleep_mode != 0) { |     } else if (packet.control.sleep_mode != 0) { | ||||||
|       this->preset = CLIMATE_PRESET_SLEEP; |       this->preset = CLIMATE_PRESET_SLEEP; | ||||||
|  |     } else if (packet.control.ten_degree != 0) { | ||||||
|  |       this->preset = CLIMATE_PRESET_AWAY; | ||||||
|     } else { |     } else { | ||||||
|       this->preset = CLIMATE_PRESET_NONE; |       this->preset = CLIMATE_PRESET_NONE; | ||||||
|     } |     } | ||||||
| @@ -882,25 +974,35 @@ void HonClimate::fill_control_messages_queue_() { | |||||||
|   // CLimate preset |   // CLimate preset | ||||||
|   { |   { | ||||||
|     uint8_t fast_mode_buf[] = {0x00, 0xFF}; |     uint8_t fast_mode_buf[] = {0x00, 0xFF}; | ||||||
|  |     uint8_t away_mode_buf[] = {0x00, 0xFF}; | ||||||
|     if (!new_power) { |     if (!new_power) { | ||||||
|       // If AC is off - no presets allowed |       // If AC is off - no presets allowed | ||||||
|       quiet_mode_buf[1] = 0x00; |       quiet_mode_buf[1] = 0x00; | ||||||
|       fast_mode_buf[1] = 0x00; |       fast_mode_buf[1] = 0x00; | ||||||
|  |       away_mode_buf[1] = 0x00; | ||||||
|     } else if (climate_control.preset.has_value()) { |     } else if (climate_control.preset.has_value()) { | ||||||
|       switch (climate_control.preset.value()) { |       switch (climate_control.preset.value()) { | ||||||
|         case CLIMATE_PRESET_NONE: |         case CLIMATE_PRESET_NONE: | ||||||
|           quiet_mode_buf[1] = 0x00; |           quiet_mode_buf[1] = 0x00; | ||||||
|           fast_mode_buf[1] = 0x00; |           fast_mode_buf[1] = 0x00; | ||||||
|  |           away_mode_buf[1] = 0x00; | ||||||
|           break; |           break; | ||||||
|         case CLIMATE_PRESET_ECO: |         case CLIMATE_PRESET_ECO: | ||||||
|           // Eco is not supported in Fan only mode |           // Eco is not supported in Fan only mode | ||||||
|           quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00; |           quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00; | ||||||
|           fast_mode_buf[1] = 0x00; |           fast_mode_buf[1] = 0x00; | ||||||
|  |           away_mode_buf[1] = 0x00; | ||||||
|           break; |           break; | ||||||
|         case CLIMATE_PRESET_BOOST: |         case CLIMATE_PRESET_BOOST: | ||||||
|           quiet_mode_buf[1] = 0x00; |           quiet_mode_buf[1] = 0x00; | ||||||
|           // Boost is not supported in Fan only mode |           // Boost is not supported in Fan only mode | ||||||
|           fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00; |           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; |           break; | ||||||
|         default: |         default: | ||||||
|           ESP_LOGE("Control", "Unsupported preset"); |           ESP_LOGE("Control", "Unsupported preset"); | ||||||
| @@ -921,6 +1023,13 @@ void HonClimate::fill_control_messages_queue_() { | |||||||
|                                            (uint8_t) hon_protocol::DataParameters::FAST_MODE, |                                            (uint8_t) hon_protocol::DataParameters::FAST_MODE, | ||||||
|                                        fast_mode_buf, 2)); |                                        fast_mode_buf, 2)); | ||||||
|     } |     } | ||||||
|  |     if (away_mode_buf[1] != 0xFF) { | ||||||
|  |       this->control_messages_queue_.push( | ||||||
|  |           haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, | ||||||
|  |                                        (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + | ||||||
|  |                                            (uint8_t) hon_protocol::DataParameters::TEN_DEGREE, | ||||||
|  |                                        away_mode_buf, 2)); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|   // Target temperature |   // Target temperature | ||||||
|   if (climate_control.target_temperature.has_value()) { |   if (climate_control.target_temperature.has_value()) { | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
|  |  | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include "esphome/components/sensor/sensor.h" | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/core/automation.h" | ||||||
| #include "haier_base.h" | #include "haier_base.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| @@ -52,6 +53,9 @@ class HonClimate : public HaierClimateBase { | |||||||
|   void start_steri_cleaning(); |   void start_steri_cleaning(); | ||||||
|   void set_extra_control_packet_bytes_size(size_t size) { this->extra_control_packet_bytes_ = size; }; |   void set_extra_control_packet_bytes_size(size_t size) { this->extra_control_packet_bytes_ = size; }; | ||||||
|   void set_control_method(HonControlMethod method) { this->control_method_ = method; }; |   void set_control_method(HonControlMethod method) { this->control_method_ = method; }; | ||||||
|  |   void add_alarm_start_callback(std::function<void(uint8_t, const char *)> &&callback); | ||||||
|  |   void add_alarm_end_callback(std::function<void(uint8_t, const char *)> &&callback); | ||||||
|  |   float get_active_alarm_count() const { return this->active_alarm_count_; } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   void set_handlers() override; |   void set_handlers() override; | ||||||
| @@ -77,8 +81,11 @@ class HonClimate : public HaierClimateBase { | |||||||
|   haier_protocol::HandlerError get_alarm_status_answer_handler_(haier_protocol::FrameType request_type, |   haier_protocol::HandlerError get_alarm_status_answer_handler_(haier_protocol::FrameType request_type, | ||||||
|                                                                 haier_protocol::FrameType message_type, |                                                                 haier_protocol::FrameType message_type, | ||||||
|                                                                 const uint8_t *data, size_t data_size); |                                                                 const uint8_t *data, size_t data_size); | ||||||
|  |   haier_protocol::HandlerError alarm_status_message_handler_(haier_protocol::FrameType type, const uint8_t *buffer, | ||||||
|  |                                                              size_t size); | ||||||
|   // Helper functions |   // Helper functions | ||||||
|   haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size); |   haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size); | ||||||
|  |   void process_alarm_message_(const uint8_t *packet, uint8_t size, bool check_new); | ||||||
|   void fill_control_messages_queue_(); |   void fill_control_messages_queue_(); | ||||||
|   void clear_control_messages_queue_(); |   void clear_control_messages_queue_(); | ||||||
|  |  | ||||||
| @@ -101,6 +108,26 @@ class HonClimate : public HaierClimateBase { | |||||||
|   HonControlMethod control_method_; |   HonControlMethod control_method_; | ||||||
|   esphome::sensor::Sensor *outdoor_sensor_; |   esphome::sensor::Sensor *outdoor_sensor_; | ||||||
|   std::queue<haier_protocol::HaierMessage> control_messages_queue_; |   std::queue<haier_protocol::HaierMessage> control_messages_queue_; | ||||||
|  |   CallbackManager<void(uint8_t, const char *)> alarm_start_callback_{}; | ||||||
|  |   CallbackManager<void(uint8_t, const char *)> alarm_end_callback_{}; | ||||||
|  |   float active_alarm_count_{NAN}; | ||||||
|  |   std::chrono::steady_clock::time_point last_alarm_request_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> { | ||||||
|  |  public: | ||||||
|  |   explicit HaierAlarmStartTrigger(HonClimate *parent) { | ||||||
|  |     parent->add_alarm_start_callback( | ||||||
|  |         [this](uint8_t alarm_code, const char *alarm_message) { this->trigger(alarm_code, alarm_message); }); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class HaierAlarmEndTrigger : public Trigger<uint8_t, const char *> { | ||||||
|  |  public: | ||||||
|  |   explicit HaierAlarmEndTrigger(HonClimate *parent) { | ||||||
|  |     parent->add_alarm_end_callback( | ||||||
|  |         [this](uint8_t alarm_code, const char *alarm_message) { this->trigger(alarm_code, alarm_message); }); | ||||||
|  |   } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace haier | }  // namespace haier | ||||||
|   | |||||||
| @@ -163,6 +163,62 @@ enum class SubcommandsControl : uint16_t { | |||||||
|                                   // content: all values like in status packet) |                                   // content: all values like in status packet) | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | const std::string HON_ALARM_MESSAGES[] = { | ||||||
|  |     "Outdoor module failure", | ||||||
|  |     "Outdoor defrost sensor failure", | ||||||
|  |     "Outdoor compressor exhaust sensor failure", | ||||||
|  |     "Outdoor EEPROM abnormality", | ||||||
|  |     "Indoor coil sensor failure", | ||||||
|  |     "Indoor-outdoor communication failure", | ||||||
|  |     "Power supply overvoltage protection", | ||||||
|  |     "Communication failure between panel and indoor unit", | ||||||
|  |     "Outdoor compressor overheat protection", | ||||||
|  |     "Outdoor environmental sensor abnormality", | ||||||
|  |     "Full water protection", | ||||||
|  |     "Indoor EEPROM failure", | ||||||
|  |     "Outdoor out air sensor failure", | ||||||
|  |     "CBD and module communication failure", | ||||||
|  |     "Indoor DC fan failure", | ||||||
|  |     "Outdoor DC fan failure", | ||||||
|  |     "Door switch failure", | ||||||
|  |     "Dust filter needs cleaning reminder", | ||||||
|  |     "Water shortage protection", | ||||||
|  |     "Humidity sensor failure", | ||||||
|  |     "Indoor temperature sensor failure", | ||||||
|  |     "Manipulator limit failure", | ||||||
|  |     "Indoor PM2.5 sensor failure", | ||||||
|  |     "Outdoor PM2.5 sensor failure", | ||||||
|  |     "Indoor heating overload/high load alarm", | ||||||
|  |     "Outdoor AC current protection", | ||||||
|  |     "Outdoor compressor operation abnormality", | ||||||
|  |     "Outdoor DC current protection", | ||||||
|  |     "Outdoor no-load failure", | ||||||
|  |     "CT current abnormality", | ||||||
|  |     "Indoor cooling freeze protection", | ||||||
|  |     "High and low pressure protection", | ||||||
|  |     "Compressor out air temperature is too high", | ||||||
|  |     "Outdoor evaporator sensor failure", | ||||||
|  |     "Outdoor cooling overload", | ||||||
|  |     "Water pump drainage failure", | ||||||
|  |     "Three-phase power supply failure", | ||||||
|  |     "Four-way valve failure", | ||||||
|  |     "External alarm/scraper flow switch failure", | ||||||
|  |     "Temperature cutoff protection alarm", | ||||||
|  |     "Different mode operation failure", | ||||||
|  |     "Electronic expansion valve failure", | ||||||
|  |     "Dual heat source sensor Tw failure", | ||||||
|  |     "Communication failure with the wired controller", | ||||||
|  |     "Indoor unit address duplication failure", | ||||||
|  |     "50Hz zero crossing failure", | ||||||
|  |     "Outdoor unit failure", | ||||||
|  |     "Formaldehyde sensor failure", | ||||||
|  |     "VOC sensor failure", | ||||||
|  |     "CO2 sensor failure", | ||||||
|  |     "Firewall failure", | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | constexpr size_t HON_ALARM_COUNT = sizeof(HON_ALARM_MESSAGES) / sizeof(HON_ALARM_MESSAGES[0]); | ||||||
|  |  | ||||||
| }  // namespace hon_protocol | }  // namespace hon_protocol | ||||||
| }  // namespace haier | }  // namespace haier | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -95,7 +95,7 @@ haier_protocol::HandlerError Smartair2Climate::messages_timeout_handler_with_cyc | |||||||
|   ESP_LOGI(TAG, "Answer timeout for command %02X, phase %s", (uint8_t) message_type, |   ESP_LOGI(TAG, "Answer timeout for command %02X, phase %s", (uint8_t) message_type, | ||||||
|            phase_to_string_(this->protocol_phase_)); |            phase_to_string_(this->protocol_phase_)); | ||||||
|   ProtocolPhases new_phase = (ProtocolPhases) ((int) this->protocol_phase_ + 1); |   ProtocolPhases new_phase = (ProtocolPhases) ((int) this->protocol_phase_ + 1); | ||||||
|   if (new_phase >= ProtocolPhases::SENDING_ALARM_STATUS_REQUEST) |   if (new_phase >= ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST) | ||||||
|     new_phase = ProtocolPhases::SENDING_INIT_1; |     new_phase = ProtocolPhases::SENDING_INIT_1; | ||||||
|   this->set_phase(new_phase); |   this->set_phase(new_phase); | ||||||
|   return haier_protocol::HandlerError::HANDLER_OK; |   return haier_protocol::HandlerError::HANDLER_OK; | ||||||
| @@ -170,9 +170,12 @@ void Smartair2Climate::process_phase(std::chrono::steady_clock::time_point now) | |||||||
|     case ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST: |     case ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST: | ||||||
|       this->set_phase(ProtocolPhases::SENDING_SIGNAL_LEVEL); |       this->set_phase(ProtocolPhases::SENDING_SIGNAL_LEVEL); | ||||||
|       break; |       break; | ||||||
|     case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: |     case ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST: | ||||||
|       this->set_phase(ProtocolPhases::SENDING_INIT_1); |       this->set_phase(ProtocolPhases::SENDING_INIT_1); | ||||||
|       break; |       break; | ||||||
|  |     case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST: | ||||||
|  |       this->set_phase(ProtocolPhases::IDLE); | ||||||
|  |       break; | ||||||
|     case ProtocolPhases::SENDING_CONTROL: |     case ProtocolPhases::SENDING_CONTROL: | ||||||
|       if (this->can_send_message() && this->is_control_message_interval_exceeded_(now)) { |       if (this->can_send_message() && this->is_control_message_interval_exceeded_(now)) { | ||||||
|         ESP_LOGI(TAG, "Sending control packet"); |         ESP_LOGI(TAG, "Sending control packet"); | ||||||
| @@ -343,19 +346,29 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() { | |||||||
|     } else if (climate_control.preset.has_value()) { |     } else if (climate_control.preset.has_value()) { | ||||||
|       switch (climate_control.preset.value()) { |       switch (climate_control.preset.value()) { | ||||||
|         case CLIMATE_PRESET_NONE: |         case CLIMATE_PRESET_NONE: | ||||||
|  |           out_data->ten_degree = 0; | ||||||
|           out_data->turbo_mode = 0; |           out_data->turbo_mode = 0; | ||||||
|           out_data->quiet_mode = 0; |           out_data->quiet_mode = 0; | ||||||
|           break; |           break; | ||||||
|         case CLIMATE_PRESET_BOOST: |         case CLIMATE_PRESET_BOOST: | ||||||
|  |           out_data->ten_degree = 0; | ||||||
|           out_data->turbo_mode = 1; |           out_data->turbo_mode = 1; | ||||||
|           out_data->quiet_mode = 0; |           out_data->quiet_mode = 0; | ||||||
|           break; |           break; | ||||||
|         case CLIMATE_PRESET_COMFORT: |         case CLIMATE_PRESET_COMFORT: | ||||||
|  |           out_data->ten_degree = 0; | ||||||
|           out_data->turbo_mode = 0; |           out_data->turbo_mode = 0; | ||||||
|           out_data->quiet_mode = 1; |           out_data->quiet_mode = 1; | ||||||
|           break; |           break; | ||||||
|  |         case CLIMATE_PRESET_AWAY: | ||||||
|  |           // Only allowed in heat mode | ||||||
|  |           out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0; | ||||||
|  |           out_data->turbo_mode = 0; | ||||||
|  |           out_data->quiet_mode = 0; | ||||||
|  |           break; | ||||||
|         default: |         default: | ||||||
|           ESP_LOGE("Control", "Unsupported preset"); |           ESP_LOGE("Control", "Unsupported preset"); | ||||||
|  |           out_data->ten_degree = 0; | ||||||
|           out_data->turbo_mode = 0; |           out_data->turbo_mode = 0; | ||||||
|           out_data->quiet_mode = 0; |           out_data->quiet_mode = 0; | ||||||
|           break; |           break; | ||||||
| @@ -381,6 +394,8 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin | |||||||
|       this->preset = CLIMATE_PRESET_BOOST; |       this->preset = CLIMATE_PRESET_BOOST; | ||||||
|     } else if (packet.control.quiet_mode != 0) { |     } else if (packet.control.quiet_mode != 0) { | ||||||
|       this->preset = CLIMATE_PRESET_COMFORT; |       this->preset = CLIMATE_PRESET_COMFORT; | ||||||
|  |     } else if (packet.control.ten_degree != 0) { | ||||||
|  |       this->preset = CLIMATE_PRESET_AWAY; | ||||||
|     } else { |     } else { | ||||||
|       this->preset = CLIMATE_PRESET_NONE; |       this->preset = CLIMATE_PRESET_NONE; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1030,7 +1030,9 @@ climate: | |||||||
|     visual: |     visual: | ||||||
|       min_temperature: 16 °C |       min_temperature: 16 °C | ||||||
|       max_temperature: 30 °C |       max_temperature: 30 °C | ||||||
|       temperature_step: 1 °C |       temperature_step: | ||||||
|  |         target_temperature: 1 | ||||||
|  |         current_temperature: 0.5 | ||||||
|     supported_modes: |     supported_modes: | ||||||
|     - 'OFF' |     - 'OFF' | ||||||
|     - HEAT_COOL |     - HEAT_COOL | ||||||
| @@ -1043,6 +1045,23 @@ climate: | |||||||
|     - VERTICAL |     - VERTICAL | ||||||
|     - HORIZONTAL |     - HORIZONTAL | ||||||
|     - BOTH |     - BOTH | ||||||
|  |     supported_presets: | ||||||
|  |     - AWAY | ||||||
|  |     - BOOST | ||||||
|  |     - ECO | ||||||
|  |     - SLEEP | ||||||
|  |     on_alarm_start: | ||||||
|  |       then: | ||||||
|  |         - logger.log: | ||||||
|  |             level: DEBUG | ||||||
|  |             format: "Alarm activated. Code: %d. Message: \"%s\"" | ||||||
|  |             args: [ code, message] | ||||||
|  |     on_alarm_end: | ||||||
|  |       then: | ||||||
|  |         - logger.log: | ||||||
|  |             level: DEBUG | ||||||
|  |             format: "Alarm deactivated. Code: %d. Message: \"%s\"" | ||||||
|  |             args: [ code, message] | ||||||
|  |  | ||||||
| sprinkler: | sprinkler: | ||||||
|   - id: yard_sprinkler_ctrlr |   - id: yard_sprinkler_ctrlr | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user