15 static const char *
const TAG =
"haier.climate";
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;
35 return hon_protocol::VerticalSwingMode::CENTER;
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;
50 return hon_protocol::HorizontalSwingMode::CENTER;
54 HonClimate::HonClimate()
88 return "56°C Steri-Clean";
98 ESP_LOGI(TAG,
"Sending self cleaning start request");
106 ESP_LOGI(TAG,
"Sending steri cleaning start request");
121 haier_protocol::FrameType message_type,
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;
130 haier_protocol::HandlerError result =
131 this->
answer_preprocess_(request_type, haier_protocol::FrameType::GET_DEVICE_VERSION, message_type,
133 if (result == haier_protocol::HandlerError::HANDLER_OK) {
136 return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE;
167 haier_protocol::FrameType message_type,
169 haier_protocol::HandlerError result =
170 this->
answer_preprocess_(request_type, haier_protocol::FrameType::GET_DEVICE_ID, message_type,
172 if (result == haier_protocol::HandlerError::HANDLER_OK) {
182 haier_protocol::FrameType message_type,
const uint8_t *data,
184 haier_protocol::HandlerError result =
185 this->
answer_preprocess_(request_type, haier_protocol::FrameType::CONTROL, message_type,
187 if (result == haier_protocol::HandlerError::HANDLER_OK) {
189 if (result != haier_protocol::HandlerError::HANDLER_OK) {
190 ESP_LOGW(TAG,
"Error %d while parsing Status packet", (
int) result);
198 ESP_LOGW(TAG,
"Status packet too small: %d (should be >= %d)", data_size,
203 ESP_LOGI(TAG,
"First HVAC status received");
238 haier_protocol::FrameType request_type, haier_protocol::FrameType message_type,
const uint8_t *data,
241 request_type, haier_protocol::FrameType::GET_MANAGEMENT_INFORMATION, message_type,
243 if (result == haier_protocol::HandlerError::HANDLER_OK) {
253 haier_protocol::FrameType message_type,
255 if (request_type == haier_protocol::FrameType::GET_ALARM_STATUS) {
256 if (message_type != haier_protocol::FrameType::GET_ALARM_STATUS_RESPONSE) {
259 return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE;
265 return haier_protocol::HandlerError::UNEXPECTED_MESSAGE;
268 return haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE;
271 return haier_protocol::HandlerError::HANDLER_OK;
274 return haier_protocol::HandlerError::UNSUPPORTED_MESSAGE;
279 const uint8_t *buffer,
size_t size) {
280 haier_protocol::HandlerError result = haier_protocol::HandlerError::HANDLER_OK;
283 result = haier_protocol::HandlerError::WRONG_MESSAGE_STRUCTURE;
286 this->
haier_protocol_.send_answer(haier_protocol::HaierMessage(haier_protocol::FrameType::CONFIRM));
294 haier_protocol::FrameType::GET_DEVICE_VERSION,
296 std::placeholders::_3, std::placeholders::_4));
298 haier_protocol::FrameType::GET_DEVICE_ID,
300 std::placeholders::_3, std::placeholders::_4));
302 haier_protocol::FrameType::CONTROL,
304 std::placeholders::_4));
306 haier_protocol::FrameType::GET_MANAGEMENT_INFORMATION,
308 std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
310 haier_protocol::FrameType::GET_ALARM_STATUS,
312 std::placeholders::_3, std::placeholders::_4));
314 haier_protocol::FrameType::REPORT_NETWORK_STATUS,
316 std::placeholders::_3, std::placeholders::_4));
318 haier_protocol::FrameType::ALARM_STATUS,
320 std::placeholders::_3));
325 ESP_LOGCONFIG(TAG,
" Protocol version: hOn");
326 ESP_LOGCONFIG(TAG,
" Control method: %d", (uint8_t) this->
control_method_);
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",
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));
360 static const haier_protocol::HaierMessage DEVICEID_REQUEST(haier_protocol::FrameType::GET_DEVICE_ID);
367 static const haier_protocol::HaierMessage STATUS_REQUEST(
369 static const haier_protocol::HaierMessage BIG_DATA_REQUEST(
383 static const haier_protocol::HaierMessage UPDATE_SIGNAL_REQUEST(
384 haier_protocol::FrameType::GET_MANAGEMENT_INFORMATION);
403 static const haier_protocol::HaierMessage ALARM_STATUS_REQUEST(haier_protocol::FrameType::GET_ALARM_STATUS);
419 ESP_LOGI(TAG,
"AC control is disabled, monitor only");
423 ESP_LOGW(TAG,
"Unsupported control method for hOn protocol!");
429 ESP_LOGW(TAG,
"Control message queue is empty!");
448 ESP_LOGW(TAG,
"SENDING_ACTION_COMMAND phase without action request!");
456 }
else if (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->
last_alarm_request_).count() >
457 ALARM_STATUS_REQUEST_INTERVAL_MS) {
462 (std::chrono::duration_cast<std::chrono::milliseconds>(now - this->
last_signal_request_).count() >
463 SIGNAL_LEVEL_UPDATE_INTERVAL_MS)) {
470 ESP_LOGE(TAG,
"Wrong protocol handler state: %s (%d), resetting communication",
479 static haier_protocol::HaierMessage power_on_message(
481 std::initializer_list<uint8_t>({0x00, 0x01}).begin(), 2);
482 return power_on_message;
484 static haier_protocol::HaierMessage power_off_message(
486 std::initializer_list<uint8_t>({0x00, 0x00}).begin(), 2);
487 return power_off_message;
495 control_out_buffer[4] = 0;
496 bool has_hvac_settings =
false;
498 has_hvac_settings =
true;
534 ESP_LOGE(
"Control",
"Unsupported climate mode");
555 ESP_LOGE(
"Control",
"Unsupported fan mode");
582 out_data->
set_point = ((int) target_temp) - 16;
583 out_data->
half_degree = (target_temp - ((int) target_temp) >= 0.49) ? 1 : 0;
626 ESP_LOGE(
"Control",
"Unsupported preset");
641 control_out_buffer[4] = 0;
644 return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
650 constexpr
size_t active_alarms_size =
sizeof(this->
active_alarms_);
651 if (size >= active_alarms_size + 2) {
653 size_t alarm_code = 0;
654 for (
int i = active_alarms_size - 1; i >= 0; 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;
664 esp_log_printf_(log_level, TAG, __LINE__,
"Alarm %s (%d): %s", alarm_status ?
"activated" :
"deactivated",
665 alarm_code, alarm_message);
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};
685 alarm_count += (float) (nibble_bits_count[packet[2 + i] & 0x0F] + nibble_bits_count[packet[2 + i] >> 4]);
697 if ((this->
sub_sensors_[(
size_t) type] !=
nullptr) && (sens ==
nullptr)) {
699 }
else if ((this->
sub_sensors_[(
size_t) type] ==
nullptr) && (sens !=
nullptr)) {
709 size_t index = (size_t) type;
717 #ifdef USE_BINARY_SENSOR 731 bool converted_value = value == 1;
732 size_t index = (size_t) type;
738 #endif // USE_BINARY_SENSOR 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];
764 #ifdef USE_BINARY_SENSOR 772 #endif // USE_BINARY_SENSOR 779 memcpy(&packet.sensors,
782 if (packet.sensors.error_status != 0) {
783 ESP_LOGW(TAG,
"HVAC error, code=0x%02X", packet.sensors.error_status);
790 (
float) (packet.sensors.outdoor_temperature + PROTOCOL_OUTDOOR_TEMPERATURE_OFFSET));
796 bool should_publish =
false;
800 if (packet.control.quiet_mode != 0) {
802 }
else if (packet.control.fast_mode != 0) {
804 }
else if (packet.control.sleep_mode != 0) {
806 }
else if (packet.control.ten_degree != 0) {
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);
835 switch (packet.control.fan_mode) {
841 ESP_LOGI(TAG,
"Fan speed Auto is not supported in Fan only AC mode, ignoring");
859 if (packet.control.ac_power != 0) {
861 bool disp_status = packet.control.display_status != 0;
877 should_publish = should_publish || (old_health_mode != this->
health_mode_);
881 if (packet.control.steri_clean == 1) {
884 }
else if (packet.control.self_cleaning_status == 1) {
892 ESP_LOGD(TAG,
"Cleaning status change: %d => %d", (uint8_t) this->
cleaning_status_, (uint8_t) new_cleaning);
898 this->cleaning_status_ = new_cleaning;
904 if (packet.control.ac_power == 0) {
908 switch (packet.control.ac_mode) {
926 should_publish = should_publish || (old_mode != this->
mode);
944 should_publish = should_publish || (old_swing_mode != this->
swing_mode);
947 if (should_publish) {
950 if (should_publish) {
951 ESP_LOGI(TAG,
"HVAC values changed");
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;
963 static uint8_t one_buf[] = {0x00, 0x01};
964 static uint8_t zero_buf[] = {0x00, 0x00};
973 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
981 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
988 uint8_t fan_mode_buf[] = {0x00, 0xFF};
989 uint8_t quiet_mode_buf[] = {0x00, 0xFF};
991 uint8_t buffer[2] = {0x00, 0x00};
1000 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1010 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1012 (uint8_t) hon_protocol::DataParameters::AC_MODE,
1020 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1022 (uint8_t) hon_protocol::DataParameters::AC_MODE,
1030 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1032 (uint8_t) hon_protocol::DataParameters::AC_MODE,
1036 quiet_mode_buf[1] = 0;
1042 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1044 (uint8_t) hon_protocol::DataParameters::AC_MODE,
1049 ESP_LOGE(
"Control",
"Unsupported climate mode");
1056 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1059 new_power ? one_buf : zero_buf, 2));
1063 uint8_t fast_mode_buf[] = {0x00, 0xFF};
1064 uint8_t away_mode_buf[] = {0x00, 0xFF};
1067 quiet_mode_buf[1] = 0x00;
1068 fast_mode_buf[1] = 0x00;
1069 away_mode_buf[1] = 0x00;
1073 quiet_mode_buf[1] = 0x00;
1074 fast_mode_buf[1] = 0x00;
1075 away_mode_buf[1] = 0x00;
1080 fast_mode_buf[1] = 0x00;
1081 away_mode_buf[1] = 0x00;
1084 quiet_mode_buf[1] = 0x00;
1087 away_mode_buf[1] = 0x00;
1090 quiet_mode_buf[1] = 0x00;
1091 fast_mode_buf[1] = 0x00;
1095 ESP_LOGE(
"Control",
"Unsupported preset");
1102 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1105 quiet_mode_buf, 2));
1109 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1116 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1124 uint8_t buffer[2] = {0x00, 0x00};
1127 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1149 ESP_LOGE(
"Control",
"Unsupported fan mode");
1152 if (fan_mode_buf[1] != 0xFF) {
1154 haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
1212 if ((sub_sensor !=
nullptr) && sub_sensor->has_state())
1213 sub_sensor->publish_state(NAN);
1215 #endif // USE_SENSOR 1222 static uint8_t counter = 0;
1223 counter = (counter + 1) % 3;
1224 return counter == 1;
HvacSettings current_hvac_settings_
value_type const & value() const
ClimateSwingMode swing_mode
The active swing mode of the climate device.
bool can_send_message() const
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
CallbackManager< void(uint8_t, const char *)> alarm_start_callback_
esphome::optional< esphome::climate::ClimatePreset > preset
CallbackManager< void(uint8_t, const char *)> alarm_end_callback_
uint8_t compressor_current[2]
haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size)
AirflowVerticalDirection vertical_direction_
std::unique_ptr< uint8_t[]> last_status_message_
constexpr int PROTOCOL_OUTDOOR_TEMPERATURE_OFFSET
float target_temperature
The target temperature of the climate device.
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)
virtual void set_phase(ProtocolPhases phase)
hon_protocol::HorizontalSwingMode get_horizontal_swing_mode(AirflowHorizontalDirection direction)
void control(const esphome::climate::ClimateCall &call) override
constexpr std::chrono::milliseconds CONTROL_MESSAGE_RETRIES_INTERVAL
uint8_t active_alarms_[8]
std::chrono::steady_clock::time_point last_status_request_
std::chrono::steady_clock::time_point last_signal_request_
ClimateMode mode
The active mode of the climate device.
void set_handlers() override
bool got_valid_outdoor_temp_
float active_alarm_count_
uint8_t vertical_swing_mode
float current_temperature
The current temperature of the climate device, as reported from the integration.
void set_sub_binary_sensor(SubBinarySensorType type, binary_sensor::BinarySensor *sens)
CleaningState cleaning_status_
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)
virtual bool prepare_pending_action()
HonControlMethod control_method_
esphome::optional< HardwareInfo > hvac_hardware_info_
void set_vertical_airflow(AirflowVerticalDirection direction)
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.
void process_phase(std::chrono::steady_clock::time_point now) override
void start_self_cleaning()
haier_protocol::ProtocolHandler haier_protocol_
uint8_t outdoor_fan_status
bool is_protocol_initialisation_interval_exceeded_(std::chrono::steady_clock::time_point now)
void add_alarm_start_callback(std::function< void(uint8_t, const char *)> &&callback)
constexpr size_t HON_ALARM_COUNT
uint8_t self_cleaning_status
void set_beeper_state(bool state)
void send_message_(const haier_protocol::HaierMessage &command, bool use_crc, uint8_t num_repeats=0, std::chrono::milliseconds interval=std::chrono::milliseconds::zero())
ProtocolPhases protocol_phase_
constexpr size_t SIGNAL_LEVEL_UPDATE_INTERVAL_MS
haier_protocol::HaierMessage get_wifi_signal_message_()
int extra_control_packet_bytes_
optional< ClimatePreset > preset
The active preset of the climate device.
void fill_control_messages_queue_()
const char * phase_to_string_(ProtocolPhases phase)
virtual bool has_state() const
Return whether this binary sensor has outputted a state.
const std::set< climate::ClimatePreset > & get_supported_presets() const
bool should_get_big_data_()
CleaningState get_cleaning_status() const
void set_sub_sensor(SubSensorType type, sensor::Sensor *sens)
void clear_control_messages_queue_()
esphome::optional< PendingAction > action_request_
void publish_state(float state)
Publish a new state to the front-end.
sensor::Sensor * sub_sensors_[(size_t) SubSensorType::SUB_SENSOR_TYPE_COUNT]
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 outdoor_in_air_temperature
esphome::optional< esphome::climate::ClimateFanMode > fan_mode
uint8_t outdoor_coil_temperature
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
void dump_config() override
void publish_state(bool state)
Publish a new state to the front-end.
AirflowHorizontalDirection
hon_protocol::VerticalSwingMode get_vertical_swing_mode(AirflowVerticalDirection direction)
AirflowHorizontalDirection get_horizontal_airflow() const
constexpr size_t ALARM_STATUS_REQUEST_INTERVAL_MS
void publish_state()
Publish the state of the climate device, to be called from integrations.
uint8_t expansion_valve_open_degree[2]
void HOT esp_log_printf_(int level, const char *tag, int line, const char *format,...)
ClimateMode
Enum for all modes a climate device can be in.
bool get_beeper_state() const
uint8_t four_way_valve_status
uint8_t horizontal_swing_mode
uint8_t outdoor_out_air_temperature
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.
uint8_t compressor_status
std::string get_cleaning_status_text() const
void update_sub_binary_sensor_(SubBinarySensorType type, uint8_t value)
virtual void process_protocol_reset()
uint8_t compressor_frequency
void set_horizontal_airflow(AirflowHorizontalDirection direction)
bool is_message_interval_exceeded_(std::chrono::steady_clock::time_point now)
std::chrono::steady_clock::time_point last_alarm_request_
bool is_status_request_interval_exceeded_(std::chrono::steady_clock::time_point now)
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...
bool has_state() const
Return whether this sensor has gotten a full state (that passed through all filters) yet...
Base class for all binary_sensor-type classes.
void process_alarm_message_(const uint8_t *packet, uint8_t size, bool check_new)
void process_protocol_reset() override
uint8_t other_modes_fan_speed_
AirflowVerticalDirection get_vertical_airflow() const
haier_protocol::HaierMessage get_power_message(bool state) override
Base-class for all sensors.
const std::string HON_ALARM_MESSAGES[]
bool prepare_pending_action() override
void start_steri_cleaning()
constexpr uint8_t CONTROL_MESSAGE_RETRIES
uint8_t indoor_electric_heating_status
uint8_t indoor_coil_temperature
bool is_control_message_interval_exceeded_(std::chrono::steady_clock::time_point now)
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_
binary_sensor::BinarySensor * sub_binary_sensors_[(size_t) SubBinarySensorType::SUB_BINARY_SENSOR_TYPE_COUNT]
esphome::climate::ClimateTraits traits_
AirflowHorizontalDirection horizontal_direction_
uint8_t indoor_fan_status
bool forced_request_status_
esphome::optional< esphome::climate::ClimateSwingMode > swing_mode
std::chrono::steady_clock::time_point last_valid_status_timestamp_
esphome::optional< esphome::climate::ClimateMode > mode
void dump_config() override