mirror of
https://github.com/esphome/esphome.git
synced 2024-11-22 11:47:30 +01:00
Added alarm processing for Haier component (hOn protocol) (#5965)
This commit is contained in:
parent
c6a37da9da
commit
b5932940ee
@ -18,6 +18,7 @@ from esphome.const import (
|
||||
CONF_SUPPORTED_SWING_MODES,
|
||||
CONF_TARGET_TEMPERATURE,
|
||||
CONF_TEMPERATURE_STEP,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_VISUAL,
|
||||
CONF_WIFI,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
@ -49,6 +50,8 @@ CONF_CONTROL_METHOD = "control_method"
|
||||
CONF_CONTROL_PACKET_SIZE = "control_packet_size"
|
||||
CONF_DISPLAY = "display"
|
||||
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_VERTICAL_AIRFLOW = "vertical_airflow"
|
||||
CONF_WIFI_SIGNAL = "wifi_signal"
|
||||
@ -85,8 +88,8 @@ AIRFLOW_HORIZONTAL_DIRECTION_OPTIONS = {
|
||||
}
|
||||
|
||||
SUPPORTED_SWING_MODES_OPTIONS = {
|
||||
"OFF": ClimateSwingMode.CLIMATE_SWING_OFF, # always available
|
||||
"VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL, # always available
|
||||
"OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
|
||||
"VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL,
|
||||
"HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL,
|
||||
"BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH,
|
||||
}
|
||||
@ -101,13 +104,15 @@ SUPPORTED_CLIMATE_MODES_OPTIONS = {
|
||||
}
|
||||
|
||||
SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = {
|
||||
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
|
||||
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
|
||||
"COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT,
|
||||
}
|
||||
|
||||
SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
|
||||
"ECO": ClimatePreset.CLIMATE_PRESET_ECO,
|
||||
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
|
||||
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
|
||||
"ECO": ClimatePreset.CLIMATE_PRESET_ECO,
|
||||
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
|
||||
}
|
||||
|
||||
@ -118,6 +123,16 @@ SUPPORTED_HON_CONTROL_METHODS = {
|
||||
"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):
|
||||
if CONF_VISUAL in config:
|
||||
@ -200,9 +215,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
): cv.boolean,
|
||||
cv.Optional(
|
||||
CONF_SUPPORTED_PRESETS,
|
||||
default=list(
|
||||
SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS.keys()
|
||||
),
|
||||
default=list(["BOOST", "COMFORT"]), # No AWAY by default
|
||||
): cv.ensure_list(
|
||||
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.Optional(
|
||||
CONF_SUPPORTED_PRESETS,
|
||||
default=list(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS.keys()),
|
||||
default=list(["BOOST", "ECO", "SLEEP"]), # No AWAY by default
|
||||
): cv.ensure_list(
|
||||
cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True)
|
||||
),
|
||||
@ -233,6 +246,20 @@ CONFIG_SCHEMA = cv.All(
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
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
|
||||
)
|
||||
)
|
||||
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
|
||||
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_2",
|
||||
"SENDING_FIRST_STATUS_REQUEST",
|
||||
"SENDING_ALARM_STATUS_REQUEST",
|
||||
"SENDING_FIRST_ALARM_STATUS_REQUEST",
|
||||
"IDLE",
|
||||
"SENDING_STATUS_REQUEST",
|
||||
"SENDING_UPDATE_SIGNAL_REQUEST",
|
||||
"SENDING_SIGNAL_LEVEL",
|
||||
"SENDING_CONTROL",
|
||||
"SENDING_ACTION_COMMAND",
|
||||
"SENDING_ALARM_STATUS_REQUEST",
|
||||
"UNKNOWN" // Should be the last!
|
||||
};
|
||||
static_assert(
|
||||
|
@ -64,7 +64,7 @@ class HaierClimateBase : public esphome::Component,
|
||||
SENDING_INIT_1 = 0,
|
||||
SENDING_INIT_2,
|
||||
SENDING_FIRST_STATUS_REQUEST,
|
||||
SENDING_ALARM_STATUS_REQUEST,
|
||||
SENDING_FIRST_ALARM_STATUS_REQUEST,
|
||||
// FUNCTIONAL STATE
|
||||
IDLE,
|
||||
SENDING_STATUS_REQUEST,
|
||||
@ -72,6 +72,7 @@ class HaierClimateBase : public esphome::Component,
|
||||
SENDING_SIGNAL_LEVEL,
|
||||
SENDING_CONTROL,
|
||||
SENDING_ACTION_COMMAND,
|
||||
SENDING_ALARM_STATUS_REQUEST,
|
||||
NUM_PROTOCOL_PHASES
|
||||
};
|
||||
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 uint8_t CONTROL_MESSAGE_RETRIES = 5;
|
||||
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) {
|
||||
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::FrameType message_type,
|
||||
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_) {
|
||||
case ProtocolPhases::SENDING_FIRST_STATUS_REQUEST:
|
||||
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;
|
||||
case ProtocolPhases::SENDING_ACTION_COMMAND:
|
||||
// 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);
|
||||
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
|
||||
this->set_phase(ProtocolPhases::IDLE);
|
||||
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);
|
||||
return haier_protocol::HandlerError::HANDLER_OK;
|
||||
} 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() {
|
||||
// Set handlers
|
||||
this->haier_protocol_.set_answer_handler(
|
||||
@ -291,6 +316,10 @@ void HonClimate::set_handlers() {
|
||||
haier_protocol::FrameType::REPORT_NETWORK_STATUS,
|
||||
std::bind(&HonClimate::report_network_status_answer_handler_, this, std::placeholders::_1, std::placeholders::_2,
|
||||
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() {
|
||||
@ -363,10 +392,12 @@ void HonClimate::process_phase(std::chrono::steady_clock::time_point now) {
|
||||
this->set_phase(ProtocolPhases::IDLE);
|
||||
break;
|
||||
#endif
|
||||
case ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST:
|
||||
case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST:
|
||||
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);
|
||||
this->send_message_(ALARM_STATUS_REQUEST, this->use_crc_);
|
||||
this->last_alarm_request_ = now;
|
||||
}
|
||||
break;
|
||||
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)) {
|
||||
this->set_phase(ProtocolPhases::SENDING_STATUS_REQUEST);
|
||||
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
|
||||
else if (this->send_wifi_signal_ &&
|
||||
(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);
|
||||
}
|
||||
#endif
|
||||
} break;
|
||||
default:
|
||||
@ -452,6 +487,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||
uint8_t control_out_buffer[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;
|
||||
control_out_buffer[4] = 0; // This byte should be cleared before setting values
|
||||
bool has_hvac_settings = false;
|
||||
if (this->current_hvac_settings_.valid) {
|
||||
has_hvac_settings = true;
|
||||
@ -552,31 +588,41 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||
out_data->quiet_mode = 0;
|
||||
out_data->fast_mode = 0;
|
||||
out_data->sleep_mode = 0;
|
||||
out_data->ten_degree = 0;
|
||||
break;
|
||||
case CLIMATE_PRESET_ECO:
|
||||
// Eco is not supported in Fan only mode
|
||||
out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
|
||||
out_data->fast_mode = 0;
|
||||
out_data->sleep_mode = 0;
|
||||
out_data->ten_degree = 0;
|
||||
break;
|
||||
case CLIMATE_PRESET_BOOST:
|
||||
out_data->quiet_mode = 0;
|
||||
// Boost is not supported in Fan only mode
|
||||
out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
|
||||
out_data->sleep_mode = 0;
|
||||
out_data->ten_degree = 0;
|
||||
break;
|
||||
case CLIMATE_PRESET_AWAY:
|
||||
out_data->quiet_mode = 0;
|
||||
out_data->fast_mode = 0;
|
||||
out_data->sleep_mode = 0;
|
||||
// 10 degrees allowed only in heat mode
|
||||
out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0;
|
||||
break;
|
||||
case CLIMATE_PRESET_SLEEP:
|
||||
out_data->quiet_mode = 0;
|
||||
out_data->fast_mode = 0;
|
||||
out_data->sleep_mode = 1;
|
||||
out_data->ten_degree = 0;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE("Control", "Unsupported preset");
|
||||
out_data->quiet_mode = 0;
|
||||
out_data->fast_mode = 0;
|
||||
out_data->sleep_mode = 0;
|
||||
out_data->ten_degree = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -595,6 +641,50 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||
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) {
|
||||
if (size < hon_protocol::HAIER_STATUS_FRAME_SIZE + this->extra_control_packet_bytes_)
|
||||
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;
|
||||
} else if (packet.control.sleep_mode != 0) {
|
||||
this->preset = CLIMATE_PRESET_SLEEP;
|
||||
} else if (packet.control.ten_degree != 0) {
|
||||
this->preset = CLIMATE_PRESET_AWAY;
|
||||
} else {
|
||||
this->preset = CLIMATE_PRESET_NONE;
|
||||
}
|
||||
@ -882,25 +974,35 @@ void HonClimate::fill_control_messages_queue_() {
|
||||
// CLimate preset
|
||||
{
|
||||
uint8_t fast_mode_buf[] = {0x00, 0xFF};
|
||||
uint8_t away_mode_buf[] = {0x00, 0xFF};
|
||||
if (!new_power) {
|
||||
// If AC is off - no presets allowed
|
||||
quiet_mode_buf[1] = 0x00;
|
||||
fast_mode_buf[1] = 0x00;
|
||||
away_mode_buf[1] = 0x00;
|
||||
} else if (climate_control.preset.has_value()) {
|
||||
switch (climate_control.preset.value()) {
|
||||
case CLIMATE_PRESET_NONE:
|
||||
quiet_mode_buf[1] = 0x00;
|
||||
fast_mode_buf[1] = 0x00;
|
||||
away_mode_buf[1] = 0x00;
|
||||
break;
|
||||
case CLIMATE_PRESET_ECO:
|
||||
// Eco is not supported in Fan only mode
|
||||
quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
|
||||
fast_mode_buf[1] = 0x00;
|
||||
away_mode_buf[1] = 0x00;
|
||||
break;
|
||||
case CLIMATE_PRESET_BOOST:
|
||||
quiet_mode_buf[1] = 0x00;
|
||||
// Boost is not supported in Fan only mode
|
||||
fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
|
||||
away_mode_buf[1] = 0x00;
|
||||
break;
|
||||
case CLIMATE_PRESET_AWAY:
|
||||
quiet_mode_buf[1] = 0x00;
|
||||
fast_mode_buf[1] = 0x00;
|
||||
away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE("Control", "Unsupported preset");
|
||||
@ -921,6 +1023,13 @@ void HonClimate::fill_control_messages_queue_() {
|
||||
(uint8_t) hon_protocol::DataParameters::FAST_MODE,
|
||||
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
|
||||
if (climate_control.target_temperature.has_value()) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "haier_base.h"
|
||||
|
||||
namespace esphome {
|
||||
@ -52,6 +53,9 @@ class HonClimate : public HaierClimateBase {
|
||||
void start_steri_cleaning();
|
||||
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 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:
|
||||
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::FrameType message_type,
|
||||
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
|
||||
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 clear_control_messages_queue_();
|
||||
|
||||
@ -101,6 +108,26 @@ class HonClimate : public HaierClimateBase {
|
||||
HonControlMethod control_method_;
|
||||
esphome::sensor::Sensor *outdoor_sensor_;
|
||||
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
|
||||
|
@ -163,6 +163,62 @@ enum class SubcommandsControl : uint16_t {
|
||||
// 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 haier
|
||||
} // 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,
|
||||
phase_to_string_(this->protocol_phase_));
|
||||
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;
|
||||
this->set_phase(new_phase);
|
||||
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:
|
||||
this->set_phase(ProtocolPhases::SENDING_SIGNAL_LEVEL);
|
||||
break;
|
||||
case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST:
|
||||
case ProtocolPhases::SENDING_FIRST_ALARM_STATUS_REQUEST:
|
||||
this->set_phase(ProtocolPhases::SENDING_INIT_1);
|
||||
break;
|
||||
case ProtocolPhases::SENDING_ALARM_STATUS_REQUEST:
|
||||
this->set_phase(ProtocolPhases::IDLE);
|
||||
break;
|
||||
case ProtocolPhases::SENDING_CONTROL:
|
||||
if (this->can_send_message() && this->is_control_message_interval_exceeded_(now)) {
|
||||
ESP_LOGI(TAG, "Sending control packet");
|
||||
@ -343,19 +346,29 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() {
|
||||
} else if (climate_control.preset.has_value()) {
|
||||
switch (climate_control.preset.value()) {
|
||||
case CLIMATE_PRESET_NONE:
|
||||
out_data->ten_degree = 0;
|
||||
out_data->turbo_mode = 0;
|
||||
out_data->quiet_mode = 0;
|
||||
break;
|
||||
case CLIMATE_PRESET_BOOST:
|
||||
out_data->ten_degree = 0;
|
||||
out_data->turbo_mode = 1;
|
||||
out_data->quiet_mode = 0;
|
||||
break;
|
||||
case CLIMATE_PRESET_COMFORT:
|
||||
out_data->ten_degree = 0;
|
||||
out_data->turbo_mode = 0;
|
||||
out_data->quiet_mode = 1;
|
||||
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:
|
||||
ESP_LOGE("Control", "Unsupported preset");
|
||||
out_data->ten_degree = 0;
|
||||
out_data->turbo_mode = 0;
|
||||
out_data->quiet_mode = 0;
|
||||
break;
|
||||
@ -381,6 +394,8 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin
|
||||
this->preset = CLIMATE_PRESET_BOOST;
|
||||
} else if (packet.control.quiet_mode != 0) {
|
||||
this->preset = CLIMATE_PRESET_COMFORT;
|
||||
} else if (packet.control.ten_degree != 0) {
|
||||
this->preset = CLIMATE_PRESET_AWAY;
|
||||
} else {
|
||||
this->preset = CLIMATE_PRESET_NONE;
|
||||
}
|
||||
|
@ -1026,11 +1026,13 @@ climate:
|
||||
wifi_signal: true
|
||||
beeper: true
|
||||
outdoor_temperature:
|
||||
name: Haier AC outdoor temperature
|
||||
name: Haier AC outdoor temperature
|
||||
visual:
|
||||
min_temperature: 16 °C
|
||||
max_temperature: 30 °C
|
||||
temperature_step: 1 °C
|
||||
temperature_step:
|
||||
target_temperature: 1
|
||||
current_temperature: 0.5
|
||||
supported_modes:
|
||||
- 'OFF'
|
||||
- HEAT_COOL
|
||||
@ -1043,6 +1045,23 @@ climate:
|
||||
- VERTICAL
|
||||
- HORIZONTAL
|
||||
- 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:
|
||||
- id: yard_sprinkler_ctrlr
|
||||
|
Loading…
Reference in New Issue
Block a user