ESPHome  2024.4.0
hon_climate.cpp
Go to the documentation of this file.
1 #include <chrono>
2 #include <string>
5 #include "esphome/core/helpers.h"
6 #include "hon_climate.h"
7 #include "hon_packet.h"
8 
9 using namespace esphome::climate;
10 using namespace esphome::uart;
11 
12 namespace esphome {
13 namespace haier {
14 
15 static const char *const TAG = "haier.climate";
16 constexpr size_t SIGNAL_LEVEL_UPDATE_INTERVAL_MS = 10000;
18 constexpr uint8_t CONTROL_MESSAGE_RETRIES = 5;
19 constexpr std::chrono::milliseconds CONTROL_MESSAGE_RETRIES_INTERVAL = std::chrono::milliseconds(500);
20 constexpr size_t ALARM_STATUS_REQUEST_INTERVAL_MS = 600000;
21 
23  switch (direction) {
24  case AirflowVerticalDirection::HEALTH_UP:
25  return hon_protocol::VerticalSwingMode::HEALTH_UP;
26  case AirflowVerticalDirection::MAX_UP:
27  return hon_protocol::VerticalSwingMode::MAX_UP;
28  case AirflowVerticalDirection::UP:
29  return hon_protocol::VerticalSwingMode::UP;
30  case AirflowVerticalDirection::DOWN:
31  return hon_protocol::VerticalSwingMode::DOWN;
32  case AirflowVerticalDirection::HEALTH_DOWN:
33  return hon_protocol::VerticalSwingMode::HEALTH_DOWN;
34  default:
35  return hon_protocol::VerticalSwingMode::CENTER;
36  }
37 }
38 
40  switch (direction) {
41  case AirflowHorizontalDirection::MAX_LEFT:
42  return hon_protocol::HorizontalSwingMode::MAX_LEFT;
43  case AirflowHorizontalDirection::LEFT:
44  return hon_protocol::HorizontalSwingMode::LEFT;
45  case AirflowHorizontalDirection::RIGHT:
46  return hon_protocol::HorizontalSwingMode::RIGHT;
47  case AirflowHorizontalDirection::MAX_RIGHT:
48  return hon_protocol::HorizontalSwingMode::MAX_RIGHT;
49  default:
50  return hon_protocol::HorizontalSwingMode::CENTER;
51  }
52 }
53 
54 HonClimate::HonClimate()
55  : cleaning_status_(CleaningState::NO_CLEANING), got_valid_outdoor_temp_(false), active_alarms_{0x00, 0x00, 0x00,
56  0x00, 0x00, 0x00,
57  0x00, 0x00} {
58  last_status_message_ = std::unique_ptr<uint8_t[]>(new uint8_t[sizeof(hon_protocol::HaierPacketControl)]);
61 }
62 
64 
66 
67 bool HonClimate::get_beeper_state() const { return this->beeper_status_; }
68 
70 
73  this->force_send_control_ = true;
74 }
75 
77 
80  this->force_send_control_ = true;
81 }
82 
84  switch (this->cleaning_status_) {
86  return "Self clean";
88  return "56°C Steri-Clean";
89  default:
90  return "No cleaning";
91  }
92 }
93 
95 
98  ESP_LOGI(TAG, "Sending self cleaning start request");
99  this->action_request_ =
101  }
102 }
103 
106  ESP_LOGI(TAG, "Sending steri cleaning start request");
107  this->action_request_ =
109  }
110 }
111 
112 void HonClimate::add_alarm_start_callback(std::function<void(uint8_t, const char *)> &&callback) {
113  this->alarm_start_callback_.add(std::move(callback));
114 }
115 
116 void HonClimate::add_alarm_end_callback(std::function<void(uint8_t, const char *)> &&callback) {
117  this->alarm_end_callback_.add(std::move(callback));
118 }
119 
120 haier_protocol::HandlerError HonClimate::get_device_version_answer_handler_(haier_protocol::FrameType request_type,
121  haier_protocol::FrameType message_type,
122  const uint8_t *data, size_t data_size) {
123  // Should check this before preprocess
124  if (message_type == haier_protocol::FrameType::INVALID) {
125  ESP_LOGW(TAG, "It looks like your ESPHome Haier climate configuration is wrong. You should use the smartAir2 "
126  "protocol instead of hOn");
128  return haier_protocol::HandlerError::INVALID_ANSWER;
129  }
130  haier_protocol::HandlerError result =
131  this->answer_preprocess_(request_type, haier_protocol::FrameType::GET_DEVICE_VERSION, message_type,
132  haier_protocol::FrameType::GET_DEVICE_VERSION_RESPONSE, ProtocolPhases::SENDING_INIT_1);
133  if (result == haier_protocol::HandlerError::HANDLER_OK) {
134  if (data_size < sizeof(hon_protocol::DeviceVersionAnswer)) {
135  // Wrong structure
136  return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE;
137  }
138  // All OK
140  char tmp[9];
141  tmp[8] = 0;
142  strncpy(tmp, answr->protocol_version, 8);
144  this->hvac_hardware_info_.value().protocol_version_ = std::string(tmp);
145  strncpy(tmp, answr->software_version, 8);
146  this->hvac_hardware_info_.value().software_version_ = std::string(tmp);
147  strncpy(tmp, answr->hardware_version, 8);
148  this->hvac_hardware_info_.value().hardware_version_ = std::string(tmp);
149  strncpy(tmp, answr->device_name, 8);
150  this->hvac_hardware_info_.value().device_name_ = std::string(tmp);
151  this->hvac_hardware_info_.value().functions_[0] = (answr->functions[1] & 0x01) != 0; // interactive mode support
152  this->hvac_hardware_info_.value().functions_[1] =
153  (answr->functions[1] & 0x02) != 0; // controller-device mode support
154  this->hvac_hardware_info_.value().functions_[2] = (answr->functions[1] & 0x04) != 0; // crc support
155  this->hvac_hardware_info_.value().functions_[3] = (answr->functions[1] & 0x08) != 0; // multiple AC support
156  this->hvac_hardware_info_.value().functions_[4] = (answr->functions[1] & 0x20) != 0; // roles support
157  this->use_crc_ = this->hvac_hardware_info_.value().functions_[2];
159  return result;
160  } else {
161  this->reset_phase_();
162  return result;
163  }
164 }
165 
166 haier_protocol::HandlerError HonClimate::get_device_id_answer_handler_(haier_protocol::FrameType request_type,
167  haier_protocol::FrameType message_type,
168  const uint8_t *data, size_t data_size) {
169  haier_protocol::HandlerError result =
170  this->answer_preprocess_(request_type, haier_protocol::FrameType::GET_DEVICE_ID, message_type,
171  haier_protocol::FrameType::GET_DEVICE_ID_RESPONSE, ProtocolPhases::SENDING_INIT_2);
172  if (result == haier_protocol::HandlerError::HANDLER_OK) {
174  return result;
175  } else {
176  this->reset_phase_();
177  return result;
178  }
179 }
180 
181 haier_protocol::HandlerError HonClimate::status_handler_(haier_protocol::FrameType request_type,
182  haier_protocol::FrameType message_type, const uint8_t *data,
183  size_t data_size) {
184  haier_protocol::HandlerError result =
185  this->answer_preprocess_(request_type, haier_protocol::FrameType::CONTROL, message_type,
186  haier_protocol::FrameType::STATUS, ProtocolPhases::UNKNOWN);
187  if (result == haier_protocol::HandlerError::HANDLER_OK) {
188  result = this->process_status_message_(data, data_size);
189  if (result != haier_protocol::HandlerError::HANDLER_OK) {
190  ESP_LOGW(TAG, "Error %d while parsing Status packet", (int) result);
191  this->reset_phase_();
192  this->action_request_.reset();
193  this->force_send_control_ = false;
194  } else {
195  if (data_size >= sizeof(hon_protocol::HaierPacketControl) + 2) {
196  memcpy(this->last_status_message_.get(), data + 2, sizeof(hon_protocol::HaierPacketControl));
197  } else {
198  ESP_LOGW(TAG, "Status packet too small: %d (should be >= %d)", data_size,
200  }
201  switch (this->protocol_phase_) {
203  ESP_LOGI(TAG, "First HVAC status received");
205  break;
207  // Do nothing, phase will be changed in process_phase
208  break;
211  break;
213  if (!this->control_messages_queue_.empty())
214  this->control_messages_queue_.pop();
215  if (this->control_messages_queue_.empty()) {
217  this->force_send_control_ = false;
218  if (this->current_hvac_settings_.valid)
220  } else {
222  }
223  break;
224  default:
225  break;
226  }
227  }
228  return result;
229  } else {
230  this->action_request_.reset();
231  this->force_send_control_ = false;
232  this->reset_phase_();
233  return result;
234  }
235 }
236 
238  haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data,
239  size_t data_size) {
240  haier_protocol::HandlerError result = this->answer_preprocess_(
241  request_type, haier_protocol::FrameType::GET_MANAGEMENT_INFORMATION, message_type,
242  haier_protocol::FrameType::GET_MANAGEMENT_INFORMATION_RESPONSE, ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST);
243  if (result == haier_protocol::HandlerError::HANDLER_OK) {
245  return result;
246  } else {
248  return result;
249  }
250 }
251 
252 haier_protocol::HandlerError HonClimate::get_alarm_status_answer_handler_(haier_protocol::FrameType request_type,
253  haier_protocol::FrameType message_type,
254  const uint8_t *data, size_t data_size) {
255  if (request_type == haier_protocol::FrameType::GET_ALARM_STATUS) {
256  if (message_type != haier_protocol::FrameType::GET_ALARM_STATUS_RESPONSE) {
257  // Unexpected answer to request
259  return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE;
260  }
263  // Don't expect this answer now
265  return haier_protocol::HandlerError::UNEXPECTED_MESSAGE;
266  }
267  if (data_size < sizeof(active_alarms_) + 2)
268  return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE;
269  this->process_alarm_message_(data, data_size, this->protocol_phase_ >= ProtocolPhases::IDLE);
271  return haier_protocol::HandlerError::HANDLER_OK;
272  } else {
274  return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE;
275  }
276 }
277 
278 haier_protocol::HandlerError HonClimate::alarm_status_message_handler_(haier_protocol::FrameType type,
279  const uint8_t *buffer, size_t size) {
280  haier_protocol::HandlerError result = haier_protocol::HandlerError::HANDLER_OK;
281  if (size < sizeof(this->active_alarms_) + 2) {
282  // Log error but confirm anyway to avoid to many messages
283  result = haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE;
284  }
285  this->process_alarm_message_(buffer, size, true);
286  this->haier_protocol_.send_answer(haier_protocol::HaierMessage(haier_protocol::FrameType::CONFIRM));
287  this->last_alarm_request_ = std::chrono::steady_clock::now();
288  return result;
289 }
290 
292  // Set handlers
293  this->haier_protocol_.set_answer_handler(
294  haier_protocol::FrameType::GET_DEVICE_VERSION,
295  std::bind(&HonClimate::get_device_version_answer_handler_, this, std::placeholders::_1, std::placeholders::_2,
296  std::placeholders::_3, std::placeholders::_4));
297  this->haier_protocol_.set_answer_handler(
298  haier_protocol::FrameType::GET_DEVICE_ID,
299  std::bind(&HonClimate::get_device_id_answer_handler_, this, std::placeholders::_1, std::placeholders::_2,
300  std::placeholders::_3, std::placeholders::_4));
301  this->haier_protocol_.set_answer_handler(
302  haier_protocol::FrameType::CONTROL,
303  std::bind(&HonClimate::status_handler_, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3,
304  std::placeholders::_4));
305  this->haier_protocol_.set_answer_handler(
306  haier_protocol::FrameType::GET_MANAGEMENT_INFORMATION,
307  std::bind(&HonClimate::get_management_information_answer_handler_, this, std::placeholders::_1,
308  std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
309  this->haier_protocol_.set_answer_handler(
310  haier_protocol::FrameType::GET_ALARM_STATUS,
311  std::bind(&HonClimate::get_alarm_status_answer_handler_, this, std::placeholders::_1, std::placeholders::_2,
312  std::placeholders::_3, std::placeholders::_4));
313  this->haier_protocol_.set_answer_handler(
314  haier_protocol::FrameType::REPORT_NETWORK_STATUS,
315  std::bind(&HonClimate::report_network_status_answer_handler_, this, std::placeholders::_1, std::placeholders::_2,
316  std::placeholders::_3, std::placeholders::_4));
317  this->haier_protocol_.set_message_handler(
318  haier_protocol::FrameType::ALARM_STATUS,
319  std::bind(&HonClimate::alarm_status_message_handler_, this, std::placeholders::_1, std::placeholders::_2,
320  std::placeholders::_3));
321 }
322 
325  ESP_LOGCONFIG(TAG, " Protocol version: hOn");
326  ESP_LOGCONFIG(TAG, " Control method: %d", (uint8_t) this->control_method_);
327  if (this->hvac_hardware_info_.has_value()) {
328  ESP_LOGCONFIG(TAG, " Device protocol version: %s", this->hvac_hardware_info_.value().protocol_version_.c_str());
329  ESP_LOGCONFIG(TAG, " Device software version: %s", this->hvac_hardware_info_.value().software_version_.c_str());
330  ESP_LOGCONFIG(TAG, " Device hardware version: %s", this->hvac_hardware_info_.value().hardware_version_.c_str());
331  ESP_LOGCONFIG(TAG, " Device name: %s", this->hvac_hardware_info_.value().device_name_.c_str());
332  ESP_LOGCONFIG(TAG, " Device features:%s%s%s%s%s",
333  (this->hvac_hardware_info_.value().functions_[0] ? " interactive" : ""),
334  (this->hvac_hardware_info_.value().functions_[1] ? " controller-device" : ""),
335  (this->hvac_hardware_info_.value().functions_[2] ? " crc" : ""),
336  (this->hvac_hardware_info_.value().functions_[3] ? " multinode" : ""),
337  (this->hvac_hardware_info_.value().functions_[4] ? " role" : ""));
338  ESP_LOGCONFIG(TAG, " Active alarms: %s", buf_to_hex(this->active_alarms_, sizeof(this->active_alarms_)).c_str());
339  }
340 }
341 
342 void HonClimate::process_phase(std::chrono::steady_clock::time_point now) {
343  switch (this->protocol_phase_) {
346  // Indicate device capabilities:
347  // bit 0 - if 1 module support interactive mode
348  // bit 1 - if 1 module support controller-device mode
349  // bit 2 - if 1 module support crc
350  // bit 3 - if 1 module support multiple devices
351  // bit 4..bit 15 - not used
352  uint8_t module_capabilities[2] = {0b00000000, 0b00000111};
353  static const haier_protocol::HaierMessage DEVICE_VERSION_REQUEST(
354  haier_protocol::FrameType::GET_DEVICE_VERSION, module_capabilities, sizeof(module_capabilities));
355  this->send_message_(DEVICE_VERSION_REQUEST, this->use_crc_);
356  }
357  break;
359  if (this->can_send_message() && this->is_message_interval_exceeded_(now)) {
360  static const haier_protocol::HaierMessage DEVICEID_REQUEST(haier_protocol::FrameType::GET_DEVICE_ID);
361  this->send_message_(DEVICEID_REQUEST, this->use_crc_);
362  }
363  break;
366  if (this->can_send_message() && this->is_message_interval_exceeded_(now)) {
367  static const haier_protocol::HaierMessage STATUS_REQUEST(
368  haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::GET_USER_DATA);
369  static const haier_protocol::HaierMessage BIG_DATA_REQUEST(
370  haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::GET_BIG_DATA);
372  (!this->should_get_big_data_())) {
373  this->send_message_(STATUS_REQUEST, this->use_crc_);
374  } else {
375  this->send_message_(BIG_DATA_REQUEST, this->use_crc_);
376  }
377  this->last_status_request_ = now;
378  }
379  break;
380 #ifdef USE_WIFI
382  if (this->can_send_message() && this->is_message_interval_exceeded_(now)) {
383  static const haier_protocol::HaierMessage UPDATE_SIGNAL_REQUEST(
384  haier_protocol::FrameType::GET_MANAGEMENT_INFORMATION);
385  this->send_message_(UPDATE_SIGNAL_REQUEST, this->use_crc_);
386  this->last_signal_request_ = now;
387  }
388  break;
390  if (this->can_send_message() && this->is_message_interval_exceeded_(now)) {
391  this->send_message_(this->get_wifi_signal_message_(), this->use_crc_);
392  }
393  break;
394 #else
398  break;
399 #endif
402  if (this->can_send_message() && this->is_message_interval_exceeded_(now)) {
403  static const haier_protocol::HaierMessage ALARM_STATUS_REQUEST(haier_protocol::FrameType::GET_ALARM_STATUS);
404  this->send_message_(ALARM_STATUS_REQUEST, this->use_crc_);
405  this->last_alarm_request_ = now;
406  }
407  break;
409  if (this->control_messages_queue_.empty()) {
410  switch (this->control_method_) {
412  haier_protocol::HaierMessage control_message = this->get_control_message();
413  this->control_messages_queue_.push(control_message);
414  } break;
417  break;
419  ESP_LOGI(TAG, "AC control is disabled, monitor only");
420  this->reset_to_idle_();
421  return;
422  default:
423  ESP_LOGW(TAG, "Unsupported control method for hOn protocol!");
424  this->reset_to_idle_();
425  return;
426  }
427  }
428  if (this->control_messages_queue_.empty()) {
429  ESP_LOGW(TAG, "Control message queue is empty!");
430  this->reset_to_idle_();
431  } else if (this->can_send_message() && this->is_control_message_interval_exceeded_(now)) {
432  ESP_LOGI(TAG, "Sending control packet, queue size %d", this->control_messages_queue_.size());
435  }
436  break;
438  if (this->action_request_.has_value()) {
439  if (this->action_request_.value().message.has_value()) {
440  this->send_message_(this->action_request_.value().message.value(), this->use_crc_);
441  this->action_request_.value().message.reset();
442  } else {
443  // Message already sent, reseting request and return to idle
444  this->action_request_.reset();
446  }
447  } else {
448  ESP_LOGW(TAG, "SENDING_ACTION_COMMAND phase without action request!");
450  }
451  break;
452  case ProtocolPhases::IDLE: {
455  this->forced_request_status_ = false;
456  } else if (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_alarm_request_).count() >
457  ALARM_STATUS_REQUEST_INTERVAL_MS) {
459  }
460 #ifdef USE_WIFI
461  else if (this->send_wifi_signal_ &&
462  (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_signal_request_).count() >
463  SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) {
465  }
466 #endif
467  } break;
468  default:
469  // Shouldn't get here
470  ESP_LOGE(TAG, "Wrong protocol handler state: %s (%d), resetting communication",
471  phase_to_string_(this->protocol_phase_), (int) this->protocol_phase_);
473  break;
474  }
475 }
476 
477 haier_protocol::HaierMessage HonClimate::get_power_message(bool state) {
478  if (state) {
479  static haier_protocol::HaierMessage power_on_message(
480  haier_protocol::FrameType::CONTROL, ((uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER) + 1,
481  std::initializer_list<uint8_t>({0x00, 0x01}).begin(), 2);
482  return power_on_message;
483  } else {
484  static haier_protocol::HaierMessage power_off_message(
485  haier_protocol::FrameType::CONTROL, ((uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER) + 1,
486  std::initializer_list<uint8_t>({0x00, 0x00}).begin(), 2);
487  return power_off_message;
488  }
489 }
490 
491 haier_protocol::HaierMessage HonClimate::get_control_message() {
492  uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)];
493  memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl));
494  hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer;
495  control_out_buffer[4] = 0; // This byte should be cleared before setting values
496  bool has_hvac_settings = false;
497  if (this->current_hvac_settings_.valid) {
498  has_hvac_settings = true;
499  HvacSettings &climate_control = this->current_hvac_settings_;
500  if (climate_control.mode.has_value()) {
501  switch (climate_control.mode.value()) {
502  case CLIMATE_MODE_OFF:
503  out_data->ac_power = 0;
504  break;
506  out_data->ac_power = 1;
507  out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::AUTO;
508  out_data->fan_mode = this->other_modes_fan_speed_;
509  break;
510  case CLIMATE_MODE_HEAT:
511  out_data->ac_power = 1;
512  out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::HEAT;
513  out_data->fan_mode = this->other_modes_fan_speed_;
514  break;
515  case CLIMATE_MODE_DRY:
516  out_data->ac_power = 1;
517  out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::DRY;
518  out_data->fan_mode = this->other_modes_fan_speed_;
519  break;
521  out_data->ac_power = 1;
522  out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::FAN;
523  out_data->fan_mode = this->fan_mode_speed_; // Auto doesn't work in fan only mode
524  // Disabling boost and eco mode for Fan only
525  out_data->quiet_mode = 0;
526  out_data->fast_mode = 0;
527  break;
528  case CLIMATE_MODE_COOL:
529  out_data->ac_power = 1;
530  out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::COOL;
531  out_data->fan_mode = this->other_modes_fan_speed_;
532  break;
533  default:
534  ESP_LOGE("Control", "Unsupported climate mode");
535  break;
536  }
537  }
538  // Set fan speed, if we are in fan mode, reject auto in fan mode
539  if (climate_control.fan_mode.has_value()) {
540  switch (climate_control.fan_mode.value()) {
541  case CLIMATE_FAN_LOW:
542  out_data->fan_mode = (uint8_t) hon_protocol::FanMode::FAN_LOW;
543  break;
544  case CLIMATE_FAN_MEDIUM:
545  out_data->fan_mode = (uint8_t) hon_protocol::FanMode::FAN_MID;
546  break;
547  case CLIMATE_FAN_HIGH:
548  out_data->fan_mode = (uint8_t) hon_protocol::FanMode::FAN_HIGH;
549  break;
550  case CLIMATE_FAN_AUTO:
551  if (mode != CLIMATE_MODE_FAN_ONLY) // if we are not in fan only mode
552  out_data->fan_mode = (uint8_t) hon_protocol::FanMode::FAN_AUTO;
553  break;
554  default:
555  ESP_LOGE("Control", "Unsupported fan mode");
556  break;
557  }
558  }
559  // Set swing mode
560  if (climate_control.swing_mode.has_value()) {
561  switch (climate_control.swing_mode.value()) {
562  case CLIMATE_SWING_OFF:
565  break;
569  break;
573  break;
574  case CLIMATE_SWING_BOTH:
577  break;
578  }
579  }
580  if (climate_control.target_temperature.has_value()) {
581  float target_temp = climate_control.target_temperature.value();
582  out_data->set_point = ((int) target_temp) - 16; // set the temperature with offset 16
583  out_data->half_degree = (target_temp - ((int) target_temp) >= 0.49) ? 1 : 0;
584  }
585  if (out_data->ac_power == 0) {
586  // If AC is off - no presets allowed
587  out_data->quiet_mode = 0;
588  out_data->fast_mode = 0;
589  out_data->sleep_mode = 0;
590  } else if (climate_control.preset.has_value()) {
591  switch (climate_control.preset.value()) {
592  case CLIMATE_PRESET_NONE:
593  out_data->quiet_mode = 0;
594  out_data->fast_mode = 0;
595  out_data->sleep_mode = 0;
596  out_data->ten_degree = 0;
597  break;
598  case CLIMATE_PRESET_ECO:
599  // Eco is not supported in Fan only mode
600  out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
601  out_data->fast_mode = 0;
602  out_data->sleep_mode = 0;
603  out_data->ten_degree = 0;
604  break;
606  out_data->quiet_mode = 0;
607  // Boost is not supported in Fan only mode
608  out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
609  out_data->sleep_mode = 0;
610  out_data->ten_degree = 0;
611  break;
612  case CLIMATE_PRESET_AWAY:
613  out_data->quiet_mode = 0;
614  out_data->fast_mode = 0;
615  out_data->sleep_mode = 0;
616  // 10 degrees allowed only in heat mode
617  out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0;
618  break;
620  out_data->quiet_mode = 0;
621  out_data->fast_mode = 0;
622  out_data->sleep_mode = 1;
623  out_data->ten_degree = 0;
624  break;
625  default:
626  ESP_LOGE("Control", "Unsupported preset");
627  out_data->quiet_mode = 0;
628  out_data->fast_mode = 0;
629  out_data->sleep_mode = 0;
630  out_data->ten_degree = 0;
631  break;
632  }
633  }
634  } else {
639  }
640  out_data->beeper_status = ((!this->beeper_status_) || (!has_hvac_settings)) ? 1 : 0;
641  control_out_buffer[4] = 0; // This byte should be cleared before setting values
642  out_data->display_status = this->display_status_ ? 1 : 0;
643  out_data->health_mode = this->health_mode_ ? 1 : 0;
644  return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
646  control_out_buffer, sizeof(hon_protocol::HaierPacketControl));
647 }
648 
649 void HonClimate::process_alarm_message_(const uint8_t *packet, uint8_t size, bool check_new) {
650  constexpr size_t active_alarms_size = sizeof(this->active_alarms_);
651  if (size >= active_alarms_size + 2) {
652  if (check_new) {
653  size_t alarm_code = 0;
654  for (int i = active_alarms_size - 1; i >= 0; i--) {
655  if (packet[2 + i] != active_alarms_[i]) {
656  uint8_t alarm_bit = 1;
657  for (int b = 0; b < 8; b++) {
658  if ((packet[2 + i] & alarm_bit) != (this->active_alarms_[i] & alarm_bit)) {
659  bool alarm_status = (packet[2 + i] & alarm_bit) != 0;
660  int log_level = alarm_status ? ESPHOME_LOG_LEVEL_WARN : ESPHOME_LOG_LEVEL_INFO;
661  const char *alarm_message = alarm_code < esphome::haier::hon_protocol::HON_ALARM_COUNT
663  : "Unknown";
664  esp_log_printf_(log_level, TAG, __LINE__, "Alarm %s (%d): %s", alarm_status ? "activated" : "deactivated",
665  alarm_code, alarm_message);
666  if (alarm_status) {
667  this->alarm_start_callback_.call(alarm_code, alarm_message);
668  this->active_alarm_count_ += 1.0f;
669  } else {
670  this->alarm_end_callback_.call(alarm_code, alarm_message);
671  this->active_alarm_count_ -= 1.0f;
672  }
673  }
674  alarm_bit <<= 1;
675  alarm_code++;
676  }
677  active_alarms_[i] = packet[2 + i];
678  } else
679  alarm_code += 8;
680  }
681  } else {
682  float alarm_count = 0.0f;
683  static uint8_t nibble_bits_count[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
684  for (size_t i = 0; i < sizeof(this->active_alarms_); i++) {
685  alarm_count += (float) (nibble_bits_count[packet[2 + i] & 0x0F] + nibble_bits_count[packet[2 + i] >> 4]);
686  }
687  this->active_alarm_count_ = alarm_count;
688  memcpy(this->active_alarms_, packet + 2, sizeof(this->active_alarms_));
689  }
690  }
691 }
692 
693 #ifdef USE_SENSOR
697  if ((this->sub_sensors_[(size_t) type] != nullptr) && (sens == nullptr)) {
698  this->big_data_sensors_--;
699  } else if ((this->sub_sensors_[(size_t) type] == nullptr) && (sens != nullptr)) {
700  this->big_data_sensors_++;
701  }
702  }
703  this->sub_sensors_[(size_t) type] = sens;
704  }
705 }
706 
709  size_t index = (size_t) type;
710  if ((this->sub_sensors_[index] != nullptr) &&
711  ((!this->sub_sensors_[index]->has_state()) || (this->sub_sensors_[index]->raw_state != value)))
712  this->sub_sensors_[index]->publish_state(value);
713  }
714 }
715 #endif // USE_SENSOR
716 
717 #ifdef USE_BINARY_SENSOR
720  if ((this->sub_binary_sensors_[(size_t) type] != nullptr) && (sens == nullptr)) {
721  this->big_data_sensors_--;
722  } else if ((this->sub_binary_sensors_[(size_t) type] == nullptr) && (sens != nullptr)) {
723  this->big_data_sensors_++;
724  }
725  this->sub_binary_sensors_[(size_t) type] = sens;
726  }
727 }
728 
730  if (value < 2) {
731  bool converted_value = value == 1;
732  size_t index = (size_t) type;
733  if ((this->sub_binary_sensors_[index] != nullptr) && ((!this->sub_binary_sensors_[index]->has_state()) ||
734  (this->sub_binary_sensors_[index]->state != converted_value)))
735  this->sub_binary_sensors_[index]->publish_state(converted_value);
736  }
737 }
738 #endif // USE_BINARY_SENSOR
739 
740 haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
741  size_t expected_size = 2 + sizeof(hon_protocol::HaierPacketControl) + sizeof(hon_protocol::HaierPacketSensors) +
743  if (size < expected_size)
744  return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE;
745  uint16_t subtype = (((uint16_t) packet_buffer[0]) << 8) + packet_buffer[1];
746  if ((subtype == 0x7D01) && (size >= expected_size + 4 + sizeof(hon_protocol::HaierPacketBigData))) {
747  // Got BigData packet
748  const hon_protocol::HaierPacketBigData *bd_packet =
749  (const hon_protocol::HaierPacketBigData *) (&packet_buffer[expected_size + 4]);
750 #ifdef USE_SENSOR
756  this->update_sub_sensor_(SubSensorType::POWER, encode_uint16(bd_packet->power[0], bd_packet->power[1]));
759  encode_uint16(bd_packet->compressor_current[0], bd_packet->compressor_current[1]) / 10.0);
760  this->update_sub_sensor_(
762  encode_uint16(bd_packet->expansion_valve_open_degree[0], bd_packet->expansion_valve_open_degree[1]) / 4095.0);
763 #endif // USE_SENSOR
764 #ifdef USE_BINARY_SENSOR
771  bd_packet->indoor_electric_heating_status);
772 #endif // USE_BINARY_SENSOR
773  }
774  struct {
777  } packet;
778  memcpy(&packet.control, packet_buffer + 2, sizeof(hon_protocol::HaierPacketControl));
779  memcpy(&packet.sensors,
780  packet_buffer + 2 + sizeof(hon_protocol::HaierPacketControl) + this->extra_control_packet_bytes_,
782  if (packet.sensors.error_status != 0) {
783  ESP_LOGW(TAG, "HVAC error, code=0x%02X", packet.sensors.error_status);
784  }
785 #ifdef USE_SENSOR
786  if ((this->sub_sensors_[(size_t) SubSensorType::OUTDOOR_TEMPERATURE] != nullptr) &&
787  (this->got_valid_outdoor_temp_ || (packet.sensors.outdoor_temperature > 0))) {
788  this->got_valid_outdoor_temp_ = true;
790  (float) (packet.sensors.outdoor_temperature + PROTOCOL_OUTDOOR_TEMPERATURE_OFFSET));
791  }
792  if ((this->sub_sensors_[(size_t) SubSensorType::HUMIDITY] != nullptr) && (packet.sensors.room_humidity <= 100)) {
793  this->update_sub_sensor_(SubSensorType::HUMIDITY, (float) packet.sensors.room_humidity);
794  }
795 #endif // USE_SENSOR
796  bool should_publish = false;
797  {
798  // Extra modes/presets
799  optional<ClimatePreset> old_preset = this->preset;
800  if (packet.control.quiet_mode != 0) {
801  this->preset = CLIMATE_PRESET_ECO;
802  } else if (packet.control.fast_mode != 0) {
804  } else if (packet.control.sleep_mode != 0) {
806  } else if (packet.control.ten_degree != 0) {
807  this->preset = CLIMATE_PRESET_AWAY;
808  } else {
809  this->preset = CLIMATE_PRESET_NONE;
810  }
811  should_publish = should_publish || (!old_preset.has_value()) || (old_preset.value() != this->preset.value());
812  }
813  {
814  // Target temperature
815  float old_target_temperature = this->target_temperature;
816  this->target_temperature = packet.control.set_point + 16.0f + ((packet.control.half_degree == 1) ? 0.5f : 0.0f);
817  should_publish = should_publish || (old_target_temperature != this->target_temperature);
818  }
819  {
820  // Current temperature
821  float old_current_temperature = this->current_temperature;
822  this->current_temperature = packet.sensors.room_temperature / 2.0f;
823  should_publish = should_publish || (old_current_temperature != this->current_temperature);
824  }
825  {
826  // Fan mode
827  optional<ClimateFanMode> old_fan_mode = this->fan_mode;
828  // remember the fan speed we last had for climate vs fan
829  if (packet.control.ac_mode == (uint8_t) hon_protocol::ConditioningMode::FAN) {
830  if (packet.control.fan_mode != (uint8_t) hon_protocol::FanMode::FAN_AUTO)
831  this->fan_mode_speed_ = packet.control.fan_mode;
832  } else {
833  this->other_modes_fan_speed_ = packet.control.fan_mode;
834  }
835  switch (packet.control.fan_mode) {
836  case (uint8_t) hon_protocol::FanMode::FAN_AUTO:
837  if (packet.control.ac_mode != (uint8_t) hon_protocol::ConditioningMode::FAN) {
838  this->fan_mode = CLIMATE_FAN_AUTO;
839  } else {
840  // Shouldn't accept fan speed auto in fan-only mode even if AC reports it
841  ESP_LOGI(TAG, "Fan speed Auto is not supported in Fan only AC mode, ignoring");
842  }
843  break;
844  case (uint8_t) hon_protocol::FanMode::FAN_MID:
846  break;
847  case (uint8_t) hon_protocol::FanMode::FAN_LOW:
848  this->fan_mode = CLIMATE_FAN_LOW;
849  break;
850  case (uint8_t) hon_protocol::FanMode::FAN_HIGH:
851  this->fan_mode = CLIMATE_FAN_HIGH;
852  break;
853  }
854  should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
855  }
856  {
857  // Display status
858  // should be before "Climate mode" because it is changing this->mode
859  if (packet.control.ac_power != 0) {
860  // if AC is off display status always ON so process it only when AC is on
861  bool disp_status = packet.control.display_status != 0;
862  if (disp_status != this->display_status_) {
863  // Do something only if display status changed
864  if (this->mode == CLIMATE_MODE_OFF) {
865  // AC just turned on from remote need to turn off display
866  this->force_send_control_ = true;
867  } else {
868  this->display_status_ = disp_status;
869  }
870  }
871  }
872  }
873  {
874  // Health mode
875  bool old_health_mode = this->health_mode_;
876  this->health_mode_ = packet.control.health_mode == 1;
877  should_publish = should_publish || (old_health_mode != this->health_mode_);
878  }
879  {
880  CleaningState new_cleaning;
881  if (packet.control.steri_clean == 1) {
882  // Steri-cleaning
883  new_cleaning = CleaningState::STERI_CLEAN;
884  } else if (packet.control.self_cleaning_status == 1) {
885  // Self-cleaning
886  new_cleaning = CleaningState::SELF_CLEAN;
887  } else {
888  // No cleaning
889  new_cleaning = CleaningState::NO_CLEANING;
890  }
891  if (new_cleaning != this->cleaning_status_) {
892  ESP_LOGD(TAG, "Cleaning status change: %d => %d", (uint8_t) this->cleaning_status_, (uint8_t) new_cleaning);
893  if (new_cleaning == CleaningState::NO_CLEANING) {
894  // Turning AC off after cleaning
895  this->action_request_ =
897  }
898  this->cleaning_status_ = new_cleaning;
899  }
900  }
901  {
902  // Climate mode
903  ClimateMode old_mode = this->mode;
904  if (packet.control.ac_power == 0) {
905  this->mode = CLIMATE_MODE_OFF;
906  } else {
907  // Check current hvac mode
908  switch (packet.control.ac_mode) {
910  this->mode = CLIMATE_MODE_COOL;
911  break;
913  this->mode = CLIMATE_MODE_HEAT;
914  break;
916  this->mode = CLIMATE_MODE_DRY;
917  break;
919  this->mode = CLIMATE_MODE_FAN_ONLY;
920  break;
923  break;
924  }
925  }
926  should_publish = should_publish || (old_mode != this->mode);
927  }
928  {
929  // Swing mode
930  ClimateSwingMode old_swing_mode = this->swing_mode;
931  if (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO) {
932  if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
934  } else {
936  }
937  } else {
938  if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
940  } else {
942  }
943  }
944  should_publish = should_publish || (old_swing_mode != this->swing_mode);
945  }
946  this->last_valid_status_timestamp_ = std::chrono::steady_clock::now();
947  if (should_publish) {
948  this->publish_state();
949  }
950  if (should_publish) {
951  ESP_LOGI(TAG, "HVAC values changed");
952  }
953  int log_level = should_publish ? ESPHOME_LOG_LEVEL_INFO : ESPHOME_LOG_LEVEL_DEBUG;
954  esp_log_printf_(log_level, TAG, __LINE__, "HVAC Mode = 0x%X", packet.control.ac_mode);
955  esp_log_printf_(log_level, TAG, __LINE__, "Fan speed Status = 0x%X", packet.control.fan_mode);
956  esp_log_printf_(log_level, TAG, __LINE__, "Horizontal Swing Status = 0x%X", packet.control.horizontal_swing_mode);
957  esp_log_printf_(log_level, TAG, __LINE__, "Vertical Swing Status = 0x%X", packet.control.vertical_swing_mode);
958  esp_log_printf_(log_level, TAG, __LINE__, "Set Point Status = 0x%X", packet.control.set_point);
959  return haier_protocol::HandlerError::HANDLER_OK;
960 }
961 
963  static uint8_t one_buf[] = {0x00, 0x01};
964  static uint8_t zero_buf[] = {0x00, 0x00};
965  if (!this->current_hvac_settings_.valid && !this->force_send_control_)
966  return;
968  HvacSettings climate_control;
969  climate_control = this->current_hvac_settings_;
970  // Beeper command
971  {
972  this->control_messages_queue_.push(
973  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
976  this->beeper_status_ ? zero_buf : one_buf, 2));
977  }
978  // Health mode
979  {
980  this->control_messages_queue_.push(
981  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
984  this->health_mode_ ? one_buf : zero_buf, 2));
985  }
986  // Climate mode
987  bool new_power = this->mode != CLIMATE_MODE_OFF;
988  uint8_t fan_mode_buf[] = {0x00, 0xFF};
989  uint8_t quiet_mode_buf[] = {0x00, 0xFF};
990  if (climate_control.mode.has_value()) {
991  uint8_t buffer[2] = {0x00, 0x00};
992  switch (climate_control.mode.value()) {
993  case CLIMATE_MODE_OFF:
994  new_power = false;
995  break;
997  new_power = true;
998  buffer[1] = (uint8_t) hon_protocol::ConditioningMode::AUTO;
999  this->control_messages_queue_.push(
1000  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1003  buffer, 2));
1004  fan_mode_buf[1] = this->other_modes_fan_speed_;
1005  break;
1006  case CLIMATE_MODE_HEAT:
1007  new_power = true;
1008  buffer[1] = (uint8_t) hon_protocol::ConditioningMode::HEAT;
1009  this->control_messages_queue_.push(
1010  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1012  (uint8_t) hon_protocol::DataParameters::AC_MODE,
1013  buffer, 2));
1014  fan_mode_buf[1] = this->other_modes_fan_speed_;
1015  break;
1016  case CLIMATE_MODE_DRY:
1017  new_power = true;
1018  buffer[1] = (uint8_t) hon_protocol::ConditioningMode::DRY;
1019  this->control_messages_queue_.push(
1020  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1022  (uint8_t) hon_protocol::DataParameters::AC_MODE,
1023  buffer, 2));
1024  fan_mode_buf[1] = this->other_modes_fan_speed_;
1025  break;
1026  case CLIMATE_MODE_FAN_ONLY:
1027  new_power = true;
1028  buffer[1] = (uint8_t) hon_protocol::ConditioningMode::FAN;
1029  this->control_messages_queue_.push(
1030  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1032  (uint8_t) hon_protocol::DataParameters::AC_MODE,
1033  buffer, 2));
1034  fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode
1035  // Disabling eco mode for Fan only
1036  quiet_mode_buf[1] = 0;
1037  break;
1038  case CLIMATE_MODE_COOL:
1039  new_power = true;
1040  buffer[1] = (uint8_t) hon_protocol::ConditioningMode::COOL;
1041  this->control_messages_queue_.push(
1042  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1044  (uint8_t) hon_protocol::DataParameters::AC_MODE,
1045  buffer, 2));
1046  fan_mode_buf[1] = this->other_modes_fan_speed_;
1047  break;
1048  default:
1049  ESP_LOGE("Control", "Unsupported climate mode");
1050  break;
1051  }
1052  }
1053  // Climate power
1054  {
1055  this->control_messages_queue_.push(
1056  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1059  new_power ? one_buf : zero_buf, 2));
1060  }
1061  // CLimate preset
1062  {
1063  uint8_t fast_mode_buf[] = {0x00, 0xFF};
1064  uint8_t away_mode_buf[] = {0x00, 0xFF};
1065  if (!new_power) {
1066  // If AC is off - no presets allowed
1067  quiet_mode_buf[1] = 0x00;
1068  fast_mode_buf[1] = 0x00;
1069  away_mode_buf[1] = 0x00;
1070  } else if (climate_control.preset.has_value()) {
1071  switch (climate_control.preset.value()) {
1072  case CLIMATE_PRESET_NONE:
1073  quiet_mode_buf[1] = 0x00;
1074  fast_mode_buf[1] = 0x00;
1075  away_mode_buf[1] = 0x00;
1076  break;
1077  case CLIMATE_PRESET_ECO:
1078  // Eco is not supported in Fan only mode
1079  quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
1080  fast_mode_buf[1] = 0x00;
1081  away_mode_buf[1] = 0x00;
1082  break;
1083  case CLIMATE_PRESET_BOOST:
1084  quiet_mode_buf[1] = 0x00;
1085  // Boost is not supported in Fan only mode
1086  fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
1087  away_mode_buf[1] = 0x00;
1088  break;
1089  case CLIMATE_PRESET_AWAY:
1090  quiet_mode_buf[1] = 0x00;
1091  fast_mode_buf[1] = 0x00;
1092  away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00;
1093  break;
1094  default:
1095  ESP_LOGE("Control", "Unsupported preset");
1096  break;
1097  }
1098  }
1099  auto presets = this->traits_.get_supported_presets();
1100  if ((quiet_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_ECO) != presets.end()))) {
1101  this->control_messages_queue_.push(
1102  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1105  quiet_mode_buf, 2));
1106  }
1107  if ((fast_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_BOOST) != presets.end()))) {
1108  this->control_messages_queue_.push(
1109  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1112  fast_mode_buf, 2));
1113  }
1114  if ((away_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_AWAY) != presets.end()))) {
1115  this->control_messages_queue_.push(
1116  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1119  away_mode_buf, 2));
1120  }
1121  }
1122  // Target temperature
1123  if (climate_control.target_temperature.has_value() && (this->mode != ClimateMode::CLIMATE_MODE_FAN_ONLY)) {
1124  uint8_t buffer[2] = {0x00, 0x00};
1125  buffer[1] = ((uint8_t) climate_control.target_temperature.value()) - 16;
1126  this->control_messages_queue_.push(
1127  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1130  buffer, 2));
1131  }
1132  // Fan mode
1133  if (climate_control.fan_mode.has_value()) {
1134  switch (climate_control.fan_mode.value()) {
1135  case CLIMATE_FAN_LOW:
1136  fan_mode_buf[1] = (uint8_t) hon_protocol::FanMode::FAN_LOW;
1137  break;
1138  case CLIMATE_FAN_MEDIUM:
1139  fan_mode_buf[1] = (uint8_t) hon_protocol::FanMode::FAN_MID;
1140  break;
1141  case CLIMATE_FAN_HIGH:
1142  fan_mode_buf[1] = (uint8_t) hon_protocol::FanMode::FAN_HIGH;
1143  break;
1144  case CLIMATE_FAN_AUTO:
1145  if (mode != CLIMATE_MODE_FAN_ONLY) // if we are not in fan only mode
1146  fan_mode_buf[1] = (uint8_t) hon_protocol::FanMode::FAN_AUTO;
1147  break;
1148  default:
1149  ESP_LOGE("Control", "Unsupported fan mode");
1150  break;
1151  }
1152  if (fan_mode_buf[1] != 0xFF) {
1153  this->control_messages_queue_.push(
1154  haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1157  fan_mode_buf, 2));
1158  }
1159  }
1160 }
1161 
1163  while (!this->control_messages_queue_.empty())
1164  this->control_messages_queue_.pop();
1165 }
1166 
1168  switch (this->action_request_.value().action) {
1170  uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)];
1171  memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl));
1172  hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer;
1173  out_data->self_cleaning_status = 1;
1174  out_data->steri_clean = 0;
1175  out_data->set_point = 0x06;
1178  out_data->ac_power = 1;
1179  out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::DRY;
1180  out_data->light_status = 0;
1181  this->action_request_.value().message = haier_protocol::HaierMessage(
1182  haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
1183  control_out_buffer, sizeof(hon_protocol::HaierPacketControl));
1184  }
1185  return true;
1187  uint8_t control_out_buffer[sizeof(hon_protocol::HaierPacketControl)];
1188  memcpy(control_out_buffer, this->last_status_message_.get(), sizeof(hon_protocol::HaierPacketControl));
1189  hon_protocol::HaierPacketControl *out_data = (hon_protocol::HaierPacketControl *) control_out_buffer;
1190  out_data->self_cleaning_status = 0;
1191  out_data->steri_clean = 1;
1192  out_data->set_point = 0x06;
1195  out_data->ac_power = 1;
1196  out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::DRY;
1197  out_data->light_status = 0;
1198  this->action_request_.value().message = haier_protocol::HaierMessage(
1199  haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
1200  control_out_buffer, sizeof(hon_protocol::HaierPacketControl));
1201  }
1202  return true;
1203  default:
1205  }
1206 }
1207 
1210 #ifdef USE_SENSOR
1211  for (auto &sub_sensor : this->sub_sensors_) {
1212  if ((sub_sensor != nullptr) && sub_sensor->has_state())
1213  sub_sensor->publish_state(NAN);
1214  }
1215 #endif // USE_SENSOR
1216  this->got_valid_outdoor_temp_ = false;
1217  this->hvac_hardware_info_.reset();
1218 }
1219 
1221  if (this->big_data_sensors_ > 0) {
1222  static uint8_t counter = 0;
1223  counter = (counter + 1) % 3;
1224  return counter == 1;
1225  }
1226  return false;
1227 }
1228 
1229 } // namespace haier
1230 } // namespace esphome
value_type const & value() const
Definition: optional.h:89
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition: climate.h:202
haier_protocol::HaierMessage get_control_message() override
haier_protocol::HandlerError get_alarm_status_answer_handler_(haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data, size_t data_size)
void update_sub_sensor_(SubSensorType type, float value)
esphome::optional< float > target_temperature
Definition: haier_base.h:115
CallbackManager< void(uint8_t, const char *)> alarm_start_callback_
Definition: hon_climate.h:157
esphome::optional< esphome::climate::ClimatePreset > preset
Definition: haier_base.h:116
CallbackManager< void(uint8_t, const char *)> alarm_end_callback_
Definition: hon_climate.h:158
haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size)
AirflowVerticalDirection vertical_direction_
Definition: hon_climate.h:150
std::unique_ptr< uint8_t[]> last_status_message_
Definition: haier_base.h:142
constexpr int PROTOCOL_OUTDOOR_TEMPERATURE_OFFSET
Definition: hon_climate.cpp:17
float target_temperature
The target temperature of the climate device.
Definition: climate.h:186
void add_alarm_end_callback(std::function< void(uint8_t, const char *)> &&callback)
haier_protocol::HandlerError report_network_status_answer_handler_(haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data, size_t data_size)
Definition: haier_base.cpp:208
virtual void set_phase(ProtocolPhases phase)
Definition: haier_base.cpp:75
hon_protocol::HorizontalSwingMode get_horizontal_swing_mode(AirflowHorizontalDirection direction)
Definition: hon_climate.cpp:39
void control(const esphome::climate::ClimateCall &call) override
Definition: haier_base.cpp:327
constexpr std::chrono::milliseconds CONTROL_MESSAGE_RETRIES_INTERVAL
Definition: hon_climate.cpp:19
std::chrono::steady_clock::time_point last_status_request_
Definition: haier_base.h:145
std::chrono::steady_clock::time_point last_signal_request_
Definition: haier_base.h:146
ClimateMode mode
The active mode of the climate device.
Definition: climate.h:173
void set_handlers() override
float current_temperature
The current temperature of the climate device, as reported from the integration.
Definition: climate.h:179
void set_sub_binary_sensor(SubBinarySensorType type, binary_sensor::BinarySensor *sens)
CleaningState cleaning_status_
Definition: hon_climate.h:148
haier_protocol::HandlerError answer_preprocess_(haier_protocol::FrameType request_message_type, haier_protocol::FrameType expected_request_message_type, haier_protocol::FrameType answer_message_type, haier_protocol::FrameType expected_answer_message_type, ProtocolPhases expected_phase)
Definition: haier_base.cpp:189
bool has_value() const
Definition: optional.h:87
HonControlMethod control_method_
Definition: hon_climate.h:155
esphome::optional< HardwareInfo > hvac_hardware_info_
Definition: hon_climate.h:152
void set_vertical_airflow(AirflowVerticalDirection direction)
Definition: hon_climate.cpp:71
haier_protocol::HandlerError get_device_id_answer_handler_(haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data, size_t data_size)
ClimateSwingMode
Enum for all modes a climate swing can be in.
Definition: climate_mode.h:70
void process_phase(std::chrono::steady_clock::time_point now) override
haier_protocol::ProtocolHandler haier_protocol_
Definition: haier_base.h:127
FanDirection direction
Definition: fan.h:37
bool is_protocol_initialisation_interval_exceeded_(std::chrono::steady_clock::time_point now)
Definition: haier_base.cpp:108
void add_alarm_start_callback(std::function< void(uint8_t, const char *)> &&callback)
constexpr size_t HON_ALARM_COUNT
Definition: hon_packet.h:248
void set_beeper_state(bool state)
Definition: hon_climate.cpp:65
void send_message_(const haier_protocol::HaierMessage &command, bool use_crc, uint8_t num_repeats=0, std::chrono::milliseconds interval=std::chrono::milliseconds::zero())
Definition: haier_base.cpp:360
constexpr size_t SIGNAL_LEVEL_UPDATE_INTERVAL_MS
Definition: hon_climate.cpp:16
haier_protocol::HaierMessage get_wifi_signal_message_()
Definition: haier_base.cpp:113
optional< ClimatePreset > preset
The active preset of the climate device.
Definition: climate.h:208
const char * phase_to_string_(ProtocolPhases phase)
Definition: haier_base.cpp:23
virtual bool has_state() const
Return whether this binary sensor has outputted a state.
const std::set< climate::ClimatePreset > & get_supported_presets() const
CleaningState get_cleaning_status() const
Definition: hon_climate.cpp:94
void set_sub_sensor(SubSensorType type, sensor::Sensor *sens)
esphome::optional< PendingAction > action_request_
Definition: haier_base.h:129
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
sensor::Sensor * sub_sensors_[(size_t) SubSensorType::SUB_SENSOR_TYPE_COUNT]
Definition: hon_climate.h:65
haier_protocol::HandlerError status_handler_(haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data, size_t data_size)
uint8_t type
esphome::optional< esphome::climate::ClimateFanMode > fan_mode
Definition: haier_base.h:113
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition: helpers.h:182
void publish_state(bool state)
Publish a new state to the front-end.
hon_protocol::VerticalSwingMode get_vertical_swing_mode(AirflowVerticalDirection direction)
Definition: hon_climate.cpp:22
AirflowHorizontalDirection get_horizontal_airflow() const
Definition: hon_climate.cpp:76
constexpr size_t ALARM_STATUS_REQUEST_INTERVAL_MS
Definition: hon_climate.cpp:20
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition: climate.cpp:395
void HOT esp_log_printf_(int level, const char *tag, int line, const char *format,...)
Definition: log.cpp:11
ClimateMode
Enum for all modes a climate device can be in.
Definition: climate_mode.h:10
haier_protocol::HandlerError get_device_version_answer_handler_(haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data, size_t data_size)
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition: climate.h:199
std::string get_cleaning_status_text() const
Definition: hon_climate.cpp:83
void update_sub_binary_sensor_(SubBinarySensorType type, uint8_t value)
void set_horizontal_airflow(AirflowHorizontalDirection direction)
Definition: hon_climate.cpp:78
bool is_message_interval_exceeded_(std::chrono::steady_clock::time_point now)
Definition: haier_base.cpp:96
std::chrono::steady_clock::time_point last_alarm_request_
Definition: hon_climate.h:160
bool is_status_request_interval_exceeded_(std::chrono::steady_clock::time_point now)
Definition: haier_base.cpp:100
haier_protocol::HandlerError alarm_status_message_handler_(haier_protocol::FrameType type, const uint8_t *buffer, size_t size)
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
bool has_state() const
Return whether this sensor has gotten a full state (that passed through all filters) yet...
Definition: sensor.cpp:97
Base class for all binary_sensor-type classes.
Definition: binary_sensor.h:37
void process_alarm_message_(const uint8_t *packet, uint8_t size, bool check_new)
void process_protocol_reset() override
AirflowVerticalDirection get_vertical_airflow() const
Definition: hon_climate.cpp:69
haier_protocol::HaierMessage get_power_message(bool state) override
Base-class for all sensors.
Definition: sensor.h:57
const std::string HON_ALARM_MESSAGES[]
Definition: hon_packet.h:194
bool prepare_pending_action() override
constexpr uint8_t CONTROL_MESSAGE_RETRIES
Definition: hon_climate.cpp:18
bool is_control_message_interval_exceeded_(std::chrono::steady_clock::time_point now)
Definition: haier_base.cpp:104
haier_protocol::HandlerError get_management_information_answer_handler_(haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data, size_t data_size)
std::queue< haier_protocol::HaierMessage > control_messages_queue_
Definition: hon_climate.h:156
binary_sensor::BinarySensor * sub_binary_sensors_[(size_t) SubBinarySensorType::SUB_BINARY_SENSOR_TYPE_COUNT]
Definition: hon_climate.h:82
esphome::climate::ClimateTraits traits_
Definition: haier_base.h:139
AirflowHorizontalDirection horizontal_direction_
Definition: hon_climate.h:151
esphome::optional< esphome::climate::ClimateSwingMode > swing_mode
Definition: haier_base.h:114
bool state
Definition: fan.h:34
std::chrono::steady_clock::time_point last_valid_status_timestamp_
Definition: haier_base.h:144
esphome::optional< esphome::climate::ClimateMode > mode
Definition: haier_base.h:112
void dump_config() override