mirror of
https://github.com/esphome/esphome.git
synced 2026-05-20 01:16:26 +08:00
Haier component updated to support new protocol variations (#5713)
Co-authored-by: Pavlo Dudnytskyi <pdudnytskyi@astrata.eu>
This commit is contained in:
@@ -38,16 +38,20 @@ PROTOCOL_MIN_TEMPERATURE = 16.0
|
||||
PROTOCOL_MAX_TEMPERATURE = 30.0
|
||||
PROTOCOL_TARGET_TEMPERATURE_STEP = 1.0
|
||||
PROTOCOL_CURRENT_TEMPERATURE_STEP = 0.5
|
||||
PROTOCOL_CONTROL_PACKET_SIZE = 10
|
||||
|
||||
CODEOWNERS = ["@paveldn"]
|
||||
AUTO_LOAD = ["sensor"]
|
||||
DEPENDENCIES = ["climate", "uart"]
|
||||
CONF_WIFI_SIGNAL = "wifi_signal"
|
||||
CONF_ALTERNATIVE_SWING_CONTROL = "alternative_swing_control"
|
||||
CONF_ANSWER_TIMEOUT = "answer_timeout"
|
||||
CONF_CONTROL_METHOD = "control_method"
|
||||
CONF_CONTROL_PACKET_SIZE = "control_packet_size"
|
||||
CONF_DISPLAY = "display"
|
||||
CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow"
|
||||
CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature"
|
||||
CONF_VERTICAL_AIRFLOW = "vertical_airflow"
|
||||
CONF_HORIZONTAL_AIRFLOW = "horizontal_airflow"
|
||||
CONF_WIFI_SIGNAL = "wifi_signal"
|
||||
|
||||
PROTOCOL_HON = "HON"
|
||||
PROTOCOL_SMARTAIR2 = "SMARTAIR2"
|
||||
@@ -107,6 +111,13 @@ SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
|
||||
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
|
||||
}
|
||||
|
||||
HonControlMethod = haier_ns.enum("HonControlMethod", True)
|
||||
SUPPORTED_HON_CONTROL_METHODS = {
|
||||
"MONITOR_ONLY": HonControlMethod.MONITOR_ONLY,
|
||||
"SET_GROUP_PARAMETERS": HonControlMethod.SET_GROUP_PARAMETERS,
|
||||
"SET_SINGLE_PARAMETER": HonControlMethod.SET_SINGLE_PARAMETER,
|
||||
}
|
||||
|
||||
|
||||
def validate_visual(config):
|
||||
if CONF_VISUAL in config:
|
||||
@@ -184,6 +195,9 @@ CONFIG_SCHEMA = cv.All(
|
||||
PROTOCOL_SMARTAIR2: BASE_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Smartair2Climate),
|
||||
cv.Optional(
|
||||
CONF_ALTERNATIVE_SWING_CONTROL, default=False
|
||||
): cv.boolean,
|
||||
cv.Optional(
|
||||
CONF_SUPPORTED_PRESETS,
|
||||
default=list(
|
||||
@@ -197,7 +211,15 @@ CONFIG_SCHEMA = cv.All(
|
||||
PROTOCOL_HON: BASE_CONFIG_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HonClimate),
|
||||
cv.Optional(
|
||||
CONF_CONTROL_METHOD, default="SET_GROUP_PARAMETERS"
|
||||
): cv.ensure_list(
|
||||
cv.enum(SUPPORTED_HON_CONTROL_METHODS, upper=True)
|
||||
),
|
||||
cv.Optional(CONF_BEEPER, default=True): cv.boolean,
|
||||
cv.Optional(
|
||||
CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE
|
||||
): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50),
|
||||
cv.Optional(
|
||||
CONF_SUPPORTED_PRESETS,
|
||||
default=list(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS.keys()),
|
||||
@@ -408,6 +430,8 @@ async def to_code(config):
|
||||
await climate.register_climate(var, config)
|
||||
|
||||
cg.add(var.set_send_wifi(config[CONF_WIFI_SIGNAL]))
|
||||
if CONF_CONTROL_METHOD in config:
|
||||
cg.add(var.set_control_method(config[CONF_CONTROL_METHOD]))
|
||||
if CONF_BEEPER in config:
|
||||
cg.add(var.set_beeper_state(config[CONF_BEEPER]))
|
||||
if CONF_DISPLAY in config:
|
||||
@@ -423,5 +447,15 @@ async def to_code(config):
|
||||
cg.add(var.set_supported_presets(config[CONF_SUPPORTED_PRESETS]))
|
||||
if CONF_ANSWER_TIMEOUT in config:
|
||||
cg.add(var.set_answer_timeout(config[CONF_ANSWER_TIMEOUT]))
|
||||
if CONF_ALTERNATIVE_SWING_CONTROL in config:
|
||||
cg.add(
|
||||
var.set_alternative_swing_control(config[CONF_ALTERNATIVE_SWING_CONTROL])
|
||||
)
|
||||
if CONF_CONTROL_PACKET_SIZE in config:
|
||||
cg.add(
|
||||
var.set_extra_control_packet_bytes_size(
|
||||
config[CONF_CONTROL_PACKET_SIZE] - PROTOCOL_CONTROL_PACKET_SIZE
|
||||
)
|
||||
)
|
||||
# https://github.com/paveldn/HaierProtocol
|
||||
cg.add_library("pavlodn/HaierProtocol", "0.9.20")
|
||||
cg.add_library("pavlodn/HaierProtocol", "0.9.24")
|
||||
|
||||
@@ -19,56 +19,45 @@ constexpr size_t STATUS_REQUEST_INTERVAL_MS = 5000;
|
||||
constexpr size_t PROTOCOL_INITIALIZATION_INTERVAL = 10000;
|
||||
constexpr size_t DEFAULT_MESSAGES_INTERVAL_MS = 2000;
|
||||
constexpr size_t CONTROL_MESSAGES_INTERVAL_MS = 400;
|
||||
constexpr size_t CONTROL_TIMEOUT_MS = 7000;
|
||||
constexpr size_t NO_COMMAND = 0xFF; // Indicate that there is no command supplied
|
||||
|
||||
#if (HAIER_LOG_LEVEL > 4)
|
||||
// To reduce size of binary this function only available when log level is Verbose
|
||||
const char *HaierClimateBase::phase_to_string_(ProtocolPhases phase) {
|
||||
static const char *phase_names[] = {
|
||||
"SENDING_INIT_1",
|
||||
"WAITING_INIT_1_ANSWER",
|
||||
"SENDING_INIT_2",
|
||||
"WAITING_INIT_2_ANSWER",
|
||||
"SENDING_FIRST_STATUS_REQUEST",
|
||||
"WAITING_FIRST_STATUS_ANSWER",
|
||||
"SENDING_ALARM_STATUS_REQUEST",
|
||||
"WAITING_ALARM_STATUS_ANSWER",
|
||||
"IDLE",
|
||||
"UNKNOWN",
|
||||
"SENDING_STATUS_REQUEST",
|
||||
"WAITING_STATUS_ANSWER",
|
||||
"SENDING_UPDATE_SIGNAL_REQUEST",
|
||||
"WAITING_UPDATE_SIGNAL_ANSWER",
|
||||
"SENDING_SIGNAL_LEVEL",
|
||||
"WAITING_SIGNAL_LEVEL_ANSWER",
|
||||
"SENDING_CONTROL",
|
||||
"WAITING_CONTROL_ANSWER",
|
||||
"SENDING_POWER_ON_COMMAND",
|
||||
"WAITING_POWER_ON_ANSWER",
|
||||
"SENDING_POWER_OFF_COMMAND",
|
||||
"WAITING_POWER_OFF_ANSWER",
|
||||
"SENDING_ACTION_COMMAND",
|
||||
"UNKNOWN" // Should be the last!
|
||||
};
|
||||
static_assert(
|
||||
(sizeof(phase_names) / sizeof(char *)) == (((int) ProtocolPhases::NUM_PROTOCOL_PHASES) + 1),
|
||||
"Wrong phase_names array size. Please, make sure that this array is aligned with the enum ProtocolPhases");
|
||||
int phase_index = (int) phase;
|
||||
if ((phase_index > (int) ProtocolPhases::NUM_PROTOCOL_PHASES) || (phase_index < 0))
|
||||
phase_index = (int) ProtocolPhases::NUM_PROTOCOL_PHASES;
|
||||
return phase_names[phase_index];
|
||||
}
|
||||
#endif
|
||||
|
||||
bool check_timeout(std::chrono::steady_clock::time_point now, std::chrono::steady_clock::time_point tpoint,
|
||||
size_t timeout) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(now - tpoint).count() > timeout;
|
||||
}
|
||||
|
||||
HaierClimateBase::HaierClimateBase()
|
||||
: haier_protocol_(*this),
|
||||
protocol_phase_(ProtocolPhases::SENDING_INIT_1),
|
||||
action_request_(ActionRequest::NO_ACTION),
|
||||
display_status_(true),
|
||||
health_mode_(false),
|
||||
force_send_control_(false),
|
||||
forced_publish_(false),
|
||||
forced_request_status_(false),
|
||||
first_control_attempt_(false),
|
||||
reset_protocol_request_(false),
|
||||
send_wifi_signal_(true) {
|
||||
send_wifi_signal_(true),
|
||||
use_crc_(false) {
|
||||
this->traits_ = climate::ClimateTraits();
|
||||
this->traits_.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT,
|
||||
climate::CLIMATE_MODE_FAN_ONLY, climate::CLIMATE_MODE_DRY,
|
||||
@@ -84,42 +73,43 @@ HaierClimateBase::~HaierClimateBase() {}
|
||||
|
||||
void HaierClimateBase::set_phase(ProtocolPhases phase) {
|
||||
if (this->protocol_phase_ != phase) {
|
||||
#if (HAIER_LOG_LEVEL > 4)
|
||||
ESP_LOGV(TAG, "Phase transition: %s => %s", phase_to_string_(this->protocol_phase_), phase_to_string_(phase));
|
||||
#else
|
||||
ESP_LOGV(TAG, "Phase transition: %d => %d", (int) this->protocol_phase_, (int) phase);
|
||||
#endif
|
||||
this->protocol_phase_ = phase;
|
||||
}
|
||||
}
|
||||
|
||||
bool HaierClimateBase::check_timeout_(std::chrono::steady_clock::time_point now,
|
||||
std::chrono::steady_clock::time_point tpoint, size_t timeout) {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(now - tpoint).count() > timeout;
|
||||
void HaierClimateBase::reset_phase_() {
|
||||
this->set_phase((this->protocol_phase_ >= ProtocolPhases::IDLE) ? ProtocolPhases::IDLE
|
||||
: ProtocolPhases::SENDING_INIT_1);
|
||||
}
|
||||
|
||||
void HaierClimateBase::reset_to_idle_() {
|
||||
this->force_send_control_ = false;
|
||||
if (this->current_hvac_settings_.valid)
|
||||
this->current_hvac_settings_.reset();
|
||||
this->forced_request_status_ = true;
|
||||
this->set_phase(ProtocolPhases::IDLE);
|
||||
this->action_request_.reset();
|
||||
}
|
||||
|
||||
bool HaierClimateBase::is_message_interval_exceeded_(std::chrono::steady_clock::time_point now) {
|
||||
return this->check_timeout_(now, this->last_request_timestamp_, DEFAULT_MESSAGES_INTERVAL_MS);
|
||||
return check_timeout(now, this->last_request_timestamp_, DEFAULT_MESSAGES_INTERVAL_MS);
|
||||
}
|
||||
|
||||
bool HaierClimateBase::is_status_request_interval_exceeded_(std::chrono::steady_clock::time_point now) {
|
||||
return this->check_timeout_(now, this->last_status_request_, STATUS_REQUEST_INTERVAL_MS);
|
||||
}
|
||||
|
||||
bool HaierClimateBase::is_control_message_timeout_exceeded_(std::chrono::steady_clock::time_point now) {
|
||||
return this->check_timeout_(now, this->control_request_timestamp_, CONTROL_TIMEOUT_MS);
|
||||
return check_timeout(now, this->last_status_request_, STATUS_REQUEST_INTERVAL_MS);
|
||||
}
|
||||
|
||||
bool HaierClimateBase::is_control_message_interval_exceeded_(std::chrono::steady_clock::time_point now) {
|
||||
return this->check_timeout_(now, this->last_request_timestamp_, CONTROL_MESSAGES_INTERVAL_MS);
|
||||
return check_timeout(now, this->last_request_timestamp_, CONTROL_MESSAGES_INTERVAL_MS);
|
||||
}
|
||||
|
||||
bool HaierClimateBase::is_protocol_initialisation_interval_exceeded_(std::chrono::steady_clock::time_point now) {
|
||||
return this->check_timeout_(now, this->last_request_timestamp_, PROTOCOL_INITIALIZATION_INTERVAL);
|
||||
return check_timeout(now, this->last_request_timestamp_, PROTOCOL_INITIALIZATION_INTERVAL);
|
||||
}
|
||||
|
||||
#ifdef USE_WIFI
|
||||
haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_(uint8_t message_type) {
|
||||
haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_() {
|
||||
static uint8_t wifi_status_data[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
if (wifi::global_wifi_component->is_connected()) {
|
||||
wifi_status_data[1] = 0;
|
||||
@@ -131,7 +121,8 @@ haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_(uint8_t
|
||||
wifi_status_data[1] = 1;
|
||||
wifi_status_data[3] = 0;
|
||||
}
|
||||
return haier_protocol::HaierMessage(message_type, wifi_status_data, sizeof(wifi_status_data));
|
||||
return haier_protocol::HaierMessage(haier_protocol::FrameType::REPORT_NETWORK_STATUS, wifi_status_data,
|
||||
sizeof(wifi_status_data));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -140,7 +131,7 @@ bool HaierClimateBase::get_display_state() const { return this->display_status_;
|
||||
void HaierClimateBase::set_display_state(bool state) {
|
||||
if (this->display_status_ != state) {
|
||||
this->display_status_ = state;
|
||||
this->set_force_send_control_(true);
|
||||
this->force_send_control_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,15 +140,24 @@ bool HaierClimateBase::get_health_mode() const { return this->health_mode_; }
|
||||
void HaierClimateBase::set_health_mode(bool state) {
|
||||
if (this->health_mode_ != state) {
|
||||
this->health_mode_ = state;
|
||||
this->set_force_send_control_(true);
|
||||
this->force_send_control_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void HaierClimateBase::send_power_on_command() { this->action_request_ = ActionRequest::TURN_POWER_ON; }
|
||||
void HaierClimateBase::send_power_on_command() {
|
||||
this->action_request_ =
|
||||
PendingAction({ActionRequest::TURN_POWER_ON, esphome::optional<haier_protocol::HaierMessage>()});
|
||||
}
|
||||
|
||||
void HaierClimateBase::send_power_off_command() { this->action_request_ = ActionRequest::TURN_POWER_OFF; }
|
||||
void HaierClimateBase::send_power_off_command() {
|
||||
this->action_request_ =
|
||||
PendingAction({ActionRequest::TURN_POWER_OFF, esphome::optional<haier_protocol::HaierMessage>()});
|
||||
}
|
||||
|
||||
void HaierClimateBase::toggle_power() { this->action_request_ = ActionRequest::TOGGLE_POWER; }
|
||||
void HaierClimateBase::toggle_power() {
|
||||
this->action_request_ =
|
||||
PendingAction({ActionRequest::TOGGLE_POWER, esphome::optional<haier_protocol::HaierMessage>()});
|
||||
}
|
||||
|
||||
void HaierClimateBase::set_supported_swing_modes(const std::set<climate::ClimateSwingMode> &modes) {
|
||||
this->traits_.set_supported_swing_modes(modes);
|
||||
@@ -165,9 +165,7 @@ void HaierClimateBase::set_supported_swing_modes(const std::set<climate::Climate
|
||||
this->traits_.add_supported_swing_mode(climate::CLIMATE_SWING_OFF);
|
||||
}
|
||||
|
||||
void HaierClimateBase::set_answer_timeout(uint32_t timeout) {
|
||||
this->answer_timeout_ = std::chrono::milliseconds(timeout);
|
||||
}
|
||||
void HaierClimateBase::set_answer_timeout(uint32_t timeout) { this->haier_protocol_.set_answer_timeout(timeout); }
|
||||
|
||||
void HaierClimateBase::set_supported_modes(const std::set<climate::ClimateMode> &modes) {
|
||||
this->traits_.set_supported_modes(modes);
|
||||
@@ -183,29 +181,42 @@ void HaierClimateBase::set_supported_presets(const std::set<climate::ClimatePres
|
||||
|
||||
void HaierClimateBase::set_send_wifi(bool send_wifi) { this->send_wifi_signal_ = send_wifi; }
|
||||
|
||||
haier_protocol::HandlerError HaierClimateBase::answer_preprocess_(uint8_t request_message_type,
|
||||
uint8_t expected_request_message_type,
|
||||
uint8_t answer_message_type,
|
||||
uint8_t expected_answer_message_type,
|
||||
ProtocolPhases expected_phase) {
|
||||
void HaierClimateBase::send_custom_command(const haier_protocol::HaierMessage &message) {
|
||||
this->action_request_ = PendingAction({ActionRequest::SEND_CUSTOM_COMMAND, message});
|
||||
}
|
||||
|
||||
haier_protocol::HandlerError HaierClimateBase::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) {
|
||||
haier_protocol::HandlerError result = haier_protocol::HandlerError::HANDLER_OK;
|
||||
if ((expected_request_message_type != NO_COMMAND) && (request_message_type != expected_request_message_type))
|
||||
if ((expected_request_message_type != haier_protocol::FrameType::UNKNOWN_FRAME_TYPE) &&
|
||||
(request_message_type != expected_request_message_type))
|
||||
result = haier_protocol::HandlerError::UNSUPPORTED_MESSAGE;
|
||||
if ((expected_answer_message_type != NO_COMMAND) && (answer_message_type != expected_answer_message_type))
|
||||
if ((expected_answer_message_type != haier_protocol::FrameType::UNKNOWN_FRAME_TYPE) &&
|
||||
(answer_message_type != expected_answer_message_type))
|
||||
result = haier_protocol::HandlerError::UNSUPPORTED_MESSAGE;
|
||||
if ((expected_phase != ProtocolPhases::UNKNOWN) && (expected_phase != this->protocol_phase_))
|
||||
if (!this->haier_protocol_.is_waiting_for_answer() ||
|
||||
((expected_phase != ProtocolPhases::UNKNOWN) && (expected_phase != this->protocol_phase_)))
|
||||
result = haier_protocol::HandlerError::UNEXPECTED_MESSAGE;
|
||||
if (is_message_invalid(answer_message_type))
|
||||
if (answer_message_type == haier_protocol::FrameType::INVALID)
|
||||
result = haier_protocol::HandlerError::INVALID_ANSWER;
|
||||
return result;
|
||||
}
|
||||
|
||||
haier_protocol::HandlerError HaierClimateBase::timeout_default_handler_(uint8_t request_type) {
|
||||
#if (HAIER_LOG_LEVEL > 4)
|
||||
ESP_LOGW(TAG, "Answer timeout for command %02X, phase %s", request_type, phase_to_string_(this->protocol_phase_));
|
||||
#else
|
||||
ESP_LOGW(TAG, "Answer timeout for command %02X, phase %d", request_type, (int) this->protocol_phase_);
|
||||
#endif
|
||||
haier_protocol::HandlerError HaierClimateBase::report_network_status_answer_handler_(
|
||||
haier_protocol::FrameType request_type, haier_protocol::FrameType message_type, const uint8_t *data,
|
||||
size_t data_size) {
|
||||
haier_protocol::HandlerError result =
|
||||
this->answer_preprocess_(request_type, haier_protocol::FrameType::REPORT_NETWORK_STATUS, message_type,
|
||||
haier_protocol::FrameType::CONFIRM, ProtocolPhases::SENDING_SIGNAL_LEVEL);
|
||||
this->set_phase(ProtocolPhases::IDLE);
|
||||
return result;
|
||||
}
|
||||
|
||||
haier_protocol::HandlerError HaierClimateBase::timeout_default_handler_(haier_protocol::FrameType request_type) {
|
||||
ESP_LOGW(TAG, "Answer timeout for command %02X, phase %s", (uint8_t) request_type,
|
||||
phase_to_string_(this->protocol_phase_));
|
||||
if (this->protocol_phase_ > ProtocolPhases::IDLE) {
|
||||
this->set_phase(ProtocolPhases::IDLE);
|
||||
} else {
|
||||
@@ -219,79 +230,95 @@ void HaierClimateBase::setup() {
|
||||
// Set timestamp here to give AC time to boot
|
||||
this->last_request_timestamp_ = std::chrono::steady_clock::now();
|
||||
this->set_phase(ProtocolPhases::SENDING_INIT_1);
|
||||
this->set_handlers();
|
||||
this->haier_protocol_.set_default_timeout_handler(
|
||||
std::bind(&esphome::haier::HaierClimateBase::timeout_default_handler_, this, std::placeholders::_1));
|
||||
this->set_handlers();
|
||||
}
|
||||
|
||||
void HaierClimateBase::dump_config() {
|
||||
LOG_CLIMATE("", "Haier Climate", this);
|
||||
ESP_LOGCONFIG(TAG, " Device communication status: %s",
|
||||
(this->protocol_phase_ >= ProtocolPhases::IDLE) ? "established" : "none");
|
||||
ESP_LOGCONFIG(TAG, " Device communication status: %s", this->valid_connection() ? "established" : "none");
|
||||
}
|
||||
|
||||
void HaierClimateBase::loop() {
|
||||
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
|
||||
if ((std::chrono::duration_cast<std::chrono::milliseconds>(now - this->last_valid_status_timestamp_).count() >
|
||||
COMMUNICATION_TIMEOUT_MS) ||
|
||||
(this->reset_protocol_request_)) {
|
||||
(this->reset_protocol_request_ && (!this->haier_protocol_.is_waiting_for_answer()))) {
|
||||
this->last_valid_status_timestamp_ = now;
|
||||
if (this->protocol_phase_ >= ProtocolPhases::IDLE) {
|
||||
// No status too long, reseting protocol
|
||||
// No need to reset protocol if we didn't pass initialization phase
|
||||
if (this->reset_protocol_request_) {
|
||||
this->reset_protocol_request_ = false;
|
||||
ESP_LOGW(TAG, "Protocol reset requested");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Communication timeout, reseting protocol");
|
||||
}
|
||||
this->last_valid_status_timestamp_ = now;
|
||||
this->set_force_send_control_(false);
|
||||
if (this->hvac_settings_.valid)
|
||||
this->hvac_settings_.reset();
|
||||
this->set_phase(ProtocolPhases::SENDING_INIT_1);
|
||||
this->process_protocol_reset();
|
||||
return;
|
||||
} else {
|
||||
// No need to reset protocol if we didn't pass initialization phase
|
||||
this->last_valid_status_timestamp_ = now;
|
||||
}
|
||||
};
|
||||
if ((this->protocol_phase_ == ProtocolPhases::IDLE) ||
|
||||
(this->protocol_phase_ == ProtocolPhases::SENDING_STATUS_REQUEST) ||
|
||||
(this->protocol_phase_ == ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST) ||
|
||||
(this->protocol_phase_ == ProtocolPhases::SENDING_SIGNAL_LEVEL)) {
|
||||
if ((!this->haier_protocol_.is_waiting_for_answer()) &&
|
||||
((this->protocol_phase_ == ProtocolPhases::IDLE) ||
|
||||
(this->protocol_phase_ == ProtocolPhases::SENDING_STATUS_REQUEST) ||
|
||||
(this->protocol_phase_ == ProtocolPhases::SENDING_UPDATE_SIGNAL_REQUEST) ||
|
||||
(this->protocol_phase_ == ProtocolPhases::SENDING_SIGNAL_LEVEL))) {
|
||||
// If control message or action is pending we should send it ASAP unless we are in initialisation
|
||||
// procedure or waiting for an answer
|
||||
if (this->action_request_ != ActionRequest::NO_ACTION) {
|
||||
this->process_pending_action();
|
||||
} else if (this->hvac_settings_.valid || this->force_send_control_) {
|
||||
if (this->action_request_.has_value() && this->prepare_pending_action()) {
|
||||
this->set_phase(ProtocolPhases::SENDING_ACTION_COMMAND);
|
||||
} else if (this->next_hvac_settings_.valid || this->force_send_control_) {
|
||||
ESP_LOGV(TAG, "Control packet is pending...");
|
||||
this->set_phase(ProtocolPhases::SENDING_CONTROL);
|
||||
if (this->next_hvac_settings_.valid) {
|
||||
this->current_hvac_settings_ = this->next_hvac_settings_;
|
||||
this->next_hvac_settings_.reset();
|
||||
} else {
|
||||
this->current_hvac_settings_.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
this->process_phase(now);
|
||||
this->haier_protocol_.loop();
|
||||
}
|
||||
|
||||
void HaierClimateBase::process_pending_action() {
|
||||
ActionRequest request = this->action_request_;
|
||||
if (this->action_request_ == ActionRequest::TOGGLE_POWER) {
|
||||
request = this->mode == CLIMATE_MODE_OFF ? ActionRequest::TURN_POWER_ON : ActionRequest::TURN_POWER_OFF;
|
||||
}
|
||||
switch (request) {
|
||||
case ActionRequest::TURN_POWER_ON:
|
||||
this->set_phase(ProtocolPhases::SENDING_POWER_ON_COMMAND);
|
||||
break;
|
||||
case ActionRequest::TURN_POWER_OFF:
|
||||
this->set_phase(ProtocolPhases::SENDING_POWER_OFF_COMMAND);
|
||||
break;
|
||||
case ActionRequest::TOGGLE_POWER:
|
||||
case ActionRequest::NO_ACTION:
|
||||
// shouldn't get here, do nothing
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unsupported action: %d", (uint8_t) this->action_request_);
|
||||
break;
|
||||
}
|
||||
this->action_request_ = ActionRequest::NO_ACTION;
|
||||
void HaierClimateBase::process_protocol_reset() {
|
||||
this->force_send_control_ = false;
|
||||
if (this->current_hvac_settings_.valid)
|
||||
this->current_hvac_settings_.reset();
|
||||
if (this->next_hvac_settings_.valid)
|
||||
this->next_hvac_settings_.reset();
|
||||
this->mode = CLIMATE_MODE_OFF;
|
||||
this->current_temperature = NAN;
|
||||
this->target_temperature = NAN;
|
||||
this->fan_mode.reset();
|
||||
this->preset.reset();
|
||||
this->publish_state();
|
||||
this->set_phase(ProtocolPhases::SENDING_INIT_1);
|
||||
}
|
||||
|
||||
bool HaierClimateBase::prepare_pending_action() {
|
||||
if (this->action_request_.has_value()) {
|
||||
switch (this->action_request_.value().action) {
|
||||
case ActionRequest::SEND_CUSTOM_COMMAND:
|
||||
return true;
|
||||
case ActionRequest::TURN_POWER_ON:
|
||||
this->action_request_.value().message = this->get_power_message(true);
|
||||
return true;
|
||||
case ActionRequest::TURN_POWER_OFF:
|
||||
this->action_request_.value().message = this->get_power_message(false);
|
||||
return true;
|
||||
case ActionRequest::TOGGLE_POWER:
|
||||
this->action_request_.value().message = this->get_power_message(this->mode == ClimateMode::CLIMATE_MODE_OFF);
|
||||
return true;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unsupported action: %d", (uint8_t) this->action_request_.value().action);
|
||||
this->action_request_.reset();
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
ClimateTraits HaierClimateBase::traits() { return traits_; }
|
||||
@@ -302,23 +329,22 @@ void HaierClimateBase::control(const ClimateCall &call) {
|
||||
ESP_LOGW(TAG, "Can't send control packet, first poll answer not received");
|
||||
return; // cancel the control, we cant do it without a poll answer.
|
||||
}
|
||||
if (this->hvac_settings_.valid) {
|
||||
ESP_LOGW(TAG, "Overriding old valid settings before they were applied!");
|
||||
if (this->current_hvac_settings_.valid) {
|
||||
ESP_LOGW(TAG, "New settings come faster then processed!");
|
||||
}
|
||||
{
|
||||
if (call.get_mode().has_value())
|
||||
this->hvac_settings_.mode = call.get_mode();
|
||||
this->next_hvac_settings_.mode = call.get_mode();
|
||||
if (call.get_fan_mode().has_value())
|
||||
this->hvac_settings_.fan_mode = call.get_fan_mode();
|
||||
this->next_hvac_settings_.fan_mode = call.get_fan_mode();
|
||||
if (call.get_swing_mode().has_value())
|
||||
this->hvac_settings_.swing_mode = call.get_swing_mode();
|
||||
this->next_hvac_settings_.swing_mode = call.get_swing_mode();
|
||||
if (call.get_target_temperature().has_value())
|
||||
this->hvac_settings_.target_temperature = call.get_target_temperature();
|
||||
this->next_hvac_settings_.target_temperature = call.get_target_temperature();
|
||||
if (call.get_preset().has_value())
|
||||
this->hvac_settings_.preset = call.get_preset();
|
||||
this->hvac_settings_.valid = true;
|
||||
this->next_hvac_settings_.preset = call.get_preset();
|
||||
this->next_hvac_settings_.valid = true;
|
||||
}
|
||||
this->first_control_attempt_ = true;
|
||||
}
|
||||
|
||||
void HaierClimateBase::HvacSettings::reset() {
|
||||
@@ -330,19 +356,9 @@ void HaierClimateBase::HvacSettings::reset() {
|
||||
this->preset.reset();
|
||||
}
|
||||
|
||||
void HaierClimateBase::set_force_send_control_(bool status) {
|
||||
this->force_send_control_ = status;
|
||||
if (status) {
|
||||
this->first_control_attempt_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void HaierClimateBase::send_message_(const haier_protocol::HaierMessage &command, bool use_crc) {
|
||||
if (this->answer_timeout_.has_value()) {
|
||||
this->haier_protocol_.send_message(command, use_crc, this->answer_timeout_.value());
|
||||
} else {
|
||||
this->haier_protocol_.send_message(command, use_crc);
|
||||
}
|
||||
void HaierClimateBase::send_message_(const haier_protocol::HaierMessage &command, bool use_crc, uint8_t num_repeats,
|
||||
std::chrono::milliseconds interval) {
|
||||
this->haier_protocol_.send_message(command, use_crc, num_repeats, interval);
|
||||
this->last_request_timestamp_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace esphome {
|
||||
namespace haier {
|
||||
|
||||
enum class ActionRequest : uint8_t {
|
||||
NO_ACTION = 0,
|
||||
SEND_CUSTOM_COMMAND = 0,
|
||||
TURN_POWER_ON = 1,
|
||||
TURN_POWER_OFF = 2,
|
||||
TOGGLE_POWER = 3,
|
||||
@@ -33,7 +33,6 @@ class HaierClimateBase : public esphome::Component,
|
||||
void control(const esphome::climate::ClimateCall &call) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return esphome::setup_priority::HARDWARE; }
|
||||
void set_fahrenheit(bool fahrenheit);
|
||||
void set_display_state(bool state);
|
||||
bool get_display_state() const;
|
||||
void set_health_mode(bool state);
|
||||
@@ -45,6 +44,7 @@ class HaierClimateBase : public esphome::Component,
|
||||
void set_supported_modes(const std::set<esphome::climate::ClimateMode> &modes);
|
||||
void set_supported_swing_modes(const std::set<esphome::climate::ClimateSwingMode> &modes);
|
||||
void set_supported_presets(const std::set<esphome::climate::ClimatePreset> &presets);
|
||||
bool valid_connection() { return this->protocol_phase_ >= ProtocolPhases::IDLE; };
|
||||
size_t available() noexcept override { return esphome::uart::UARTDevice::available(); };
|
||||
size_t read_array(uint8_t *data, size_t len) noexcept override {
|
||||
return esphome::uart::UARTDevice::read_array(data, len) ? len : 0;
|
||||
@@ -55,63 +55,56 @@ class HaierClimateBase : public esphome::Component,
|
||||
bool can_send_message() const { return haier_protocol_.get_outgoing_queue_size() == 0; };
|
||||
void set_answer_timeout(uint32_t timeout);
|
||||
void set_send_wifi(bool send_wifi);
|
||||
void send_custom_command(const haier_protocol::HaierMessage &message);
|
||||
|
||||
protected:
|
||||
enum class ProtocolPhases {
|
||||
UNKNOWN = -1,
|
||||
// INITIALIZATION
|
||||
SENDING_INIT_1 = 0,
|
||||
WAITING_INIT_1_ANSWER = 1,
|
||||
SENDING_INIT_2 = 2,
|
||||
WAITING_INIT_2_ANSWER = 3,
|
||||
SENDING_FIRST_STATUS_REQUEST = 4,
|
||||
WAITING_FIRST_STATUS_ANSWER = 5,
|
||||
SENDING_ALARM_STATUS_REQUEST = 6,
|
||||
WAITING_ALARM_STATUS_ANSWER = 7,
|
||||
SENDING_INIT_2,
|
||||
SENDING_FIRST_STATUS_REQUEST,
|
||||
SENDING_ALARM_STATUS_REQUEST,
|
||||
// FUNCTIONAL STATE
|
||||
IDLE = 8,
|
||||
SENDING_STATUS_REQUEST = 10,
|
||||
WAITING_STATUS_ANSWER = 11,
|
||||
SENDING_UPDATE_SIGNAL_REQUEST = 12,
|
||||
WAITING_UPDATE_SIGNAL_ANSWER = 13,
|
||||
SENDING_SIGNAL_LEVEL = 14,
|
||||
WAITING_SIGNAL_LEVEL_ANSWER = 15,
|
||||
SENDING_CONTROL = 16,
|
||||
WAITING_CONTROL_ANSWER = 17,
|
||||
SENDING_POWER_ON_COMMAND = 18,
|
||||
WAITING_POWER_ON_ANSWER = 19,
|
||||
SENDING_POWER_OFF_COMMAND = 20,
|
||||
WAITING_POWER_OFF_ANSWER = 21,
|
||||
IDLE,
|
||||
SENDING_STATUS_REQUEST,
|
||||
SENDING_UPDATE_SIGNAL_REQUEST,
|
||||
SENDING_SIGNAL_LEVEL,
|
||||
SENDING_CONTROL,
|
||||
SENDING_ACTION_COMMAND,
|
||||
NUM_PROTOCOL_PHASES
|
||||
};
|
||||
#if (HAIER_LOG_LEVEL > 4)
|
||||
const char *phase_to_string_(ProtocolPhases phase);
|
||||
#endif
|
||||
virtual void set_handlers() = 0;
|
||||
virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
|
||||
virtual haier_protocol::HaierMessage get_control_message() = 0;
|
||||
virtual bool is_message_invalid(uint8_t message_type) = 0;
|
||||
virtual void process_pending_action();
|
||||
virtual haier_protocol::HaierMessage get_power_message(bool state) = 0;
|
||||
virtual bool prepare_pending_action();
|
||||
virtual void process_protocol_reset();
|
||||
esphome::climate::ClimateTraits traits() override;
|
||||
// Answers handlers
|
||||
haier_protocol::HandlerError answer_preprocess_(uint8_t request_message_type, uint8_t expected_request_message_type,
|
||||
uint8_t answer_message_type, uint8_t expected_answer_message_type,
|
||||
// Answer handlers
|
||||
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);
|
||||
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);
|
||||
// Timeout handler
|
||||
haier_protocol::HandlerError timeout_default_handler_(uint8_t request_type);
|
||||
haier_protocol::HandlerError timeout_default_handler_(haier_protocol::FrameType request_type);
|
||||
// Helper functions
|
||||
void set_force_send_control_(bool status);
|
||||
void send_message_(const haier_protocol::HaierMessage &command, bool use_crc);
|
||||
void send_message_(const haier_protocol::HaierMessage &command, bool use_crc, uint8_t num_repeats = 0,
|
||||
std::chrono::milliseconds interval = std::chrono::milliseconds::zero());
|
||||
virtual void set_phase(ProtocolPhases phase);
|
||||
bool check_timeout_(std::chrono::steady_clock::time_point now, std::chrono::steady_clock::time_point tpoint,
|
||||
size_t timeout);
|
||||
void reset_phase_();
|
||||
void reset_to_idle_();
|
||||
bool is_message_interval_exceeded_(std::chrono::steady_clock::time_point now);
|
||||
bool is_status_request_interval_exceeded_(std::chrono::steady_clock::time_point now);
|
||||
bool is_control_message_timeout_exceeded_(std::chrono::steady_clock::time_point now);
|
||||
bool is_control_message_interval_exceeded_(std::chrono::steady_clock::time_point now);
|
||||
bool is_protocol_initialisation_interval_exceeded_(std::chrono::steady_clock::time_point now);
|
||||
#ifdef USE_WIFI
|
||||
haier_protocol::HaierMessage get_wifi_signal_message_(uint8_t message_type);
|
||||
haier_protocol::HaierMessage get_wifi_signal_message_();
|
||||
#endif
|
||||
|
||||
struct HvacSettings {
|
||||
@@ -122,29 +115,34 @@ class HaierClimateBase : public esphome::Component,
|
||||
esphome::optional<esphome::climate::ClimatePreset> preset;
|
||||
bool valid;
|
||||
HvacSettings() : valid(false){};
|
||||
HvacSettings(const HvacSettings &) = default;
|
||||
HvacSettings &operator=(const HvacSettings &) = default;
|
||||
void reset();
|
||||
};
|
||||
struct PendingAction {
|
||||
ActionRequest action;
|
||||
esphome::optional<haier_protocol::HaierMessage> message;
|
||||
};
|
||||
haier_protocol::ProtocolHandler haier_protocol_;
|
||||
ProtocolPhases protocol_phase_;
|
||||
ActionRequest action_request_;
|
||||
esphome::optional<PendingAction> action_request_;
|
||||
uint8_t fan_mode_speed_;
|
||||
uint8_t other_modes_fan_speed_;
|
||||
bool display_status_;
|
||||
bool health_mode_;
|
||||
bool force_send_control_;
|
||||
bool forced_publish_;
|
||||
bool forced_request_status_;
|
||||
bool first_control_attempt_;
|
||||
bool reset_protocol_request_;
|
||||
bool send_wifi_signal_;
|
||||
bool use_crc_;
|
||||
esphome::climate::ClimateTraits traits_;
|
||||
HvacSettings hvac_settings_;
|
||||
HvacSettings current_hvac_settings_;
|
||||
HvacSettings next_hvac_settings_;
|
||||
std::unique_ptr<uint8_t[]> last_status_message_;
|
||||
std::chrono::steady_clock::time_point last_request_timestamp_; // For interval between messages
|
||||
std::chrono::steady_clock::time_point last_valid_status_timestamp_; // For protocol timeout
|
||||
std::chrono::steady_clock::time_point last_status_request_; // To request AC status
|
||||
std::chrono::steady_clock::time_point control_request_timestamp_; // To send control message
|
||||
optional<std::chrono::milliseconds> answer_timeout_; // Message answer timeout
|
||||
bool send_wifi_signal_;
|
||||
std::chrono::steady_clock::time_point last_signal_request_; // To send WiFI signal level
|
||||
std::chrono::steady_clock::time_point last_signal_request_; // To send WiFI signal level
|
||||
};
|
||||
|
||||
} // namespace haier
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,8 @@ enum class CleaningState : uint8_t {
|
||||
STERI_CLEAN = 2,
|
||||
};
|
||||
|
||||
enum class HonControlMethod { MONITOR_ONLY = 0, SET_GROUP_PARAMETERS, SET_SINGLE_PARAMETER };
|
||||
|
||||
class HonClimate : public HaierClimateBase {
|
||||
public:
|
||||
HonClimate();
|
||||
@@ -48,44 +50,57 @@ class HonClimate : public HaierClimateBase {
|
||||
CleaningState get_cleaning_status() const;
|
||||
void start_self_cleaning();
|
||||
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; };
|
||||
|
||||
protected:
|
||||
void set_handlers() override;
|
||||
void process_phase(std::chrono::steady_clock::time_point now) override;
|
||||
haier_protocol::HaierMessage get_control_message() override;
|
||||
bool is_message_invalid(uint8_t message_type) override;
|
||||
void process_pending_action() override;
|
||||
haier_protocol::HaierMessage get_power_message(bool state) override;
|
||||
bool prepare_pending_action() override;
|
||||
void process_protocol_reset() override;
|
||||
|
||||
// Answers handlers
|
||||
haier_protocol::HandlerError get_device_version_answer_handler_(uint8_t request_type, uint8_t message_type,
|
||||
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);
|
||||
haier_protocol::HandlerError get_device_id_answer_handler_(uint8_t request_type, uint8_t message_type,
|
||||
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);
|
||||
haier_protocol::HandlerError status_handler_(uint8_t request_type, uint8_t message_type, const uint8_t *data,
|
||||
haier_protocol::HandlerError status_handler_(haier_protocol::FrameType request_type,
|
||||
haier_protocol::FrameType message_type, const uint8_t *data,
|
||||
size_t data_size);
|
||||
haier_protocol::HandlerError get_management_information_answer_handler_(uint8_t request_type, uint8_t message_type,
|
||||
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);
|
||||
haier_protocol::HandlerError report_network_status_answer_handler_(uint8_t request_type, uint8_t message_type,
|
||||
const uint8_t *data, size_t data_size);
|
||||
haier_protocol::HandlerError get_alarm_status_answer_handler_(uint8_t request_type, uint8_t message_type,
|
||||
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);
|
||||
// Helper functions
|
||||
haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size);
|
||||
std::unique_ptr<uint8_t[]> last_status_message_;
|
||||
void fill_control_messages_queue_();
|
||||
void clear_control_messages_queue_();
|
||||
|
||||
struct HardwareInfo {
|
||||
std::string protocol_version_;
|
||||
std::string software_version_;
|
||||
std::string hardware_version_;
|
||||
std::string device_name_;
|
||||
bool functions_[5];
|
||||
};
|
||||
|
||||
bool beeper_status_;
|
||||
CleaningState cleaning_status_;
|
||||
bool got_valid_outdoor_temp_;
|
||||
AirflowVerticalDirection vertical_direction_;
|
||||
AirflowHorizontalDirection horizontal_direction_;
|
||||
bool hvac_hardware_info_available_;
|
||||
std::string hvac_protocol_version_;
|
||||
std::string hvac_software_version_;
|
||||
std::string hvac_hardware_version_;
|
||||
std::string hvac_device_name_;
|
||||
bool hvac_functions_[5];
|
||||
bool &use_crc_;
|
||||
esphome::optional<HardwareInfo> hvac_hardware_info_;
|
||||
uint8_t active_alarms_[8];
|
||||
int extra_control_packet_bytes_;
|
||||
HonControlMethod control_method_;
|
||||
esphome::sensor::Sensor *outdoor_sensor_;
|
||||
std::queue<haier_protocol::HaierMessage> control_messages_queue_;
|
||||
};
|
||||
|
||||
} // namespace haier
|
||||
|
||||
@@ -35,6 +35,20 @@ enum class ConditioningMode : uint8_t {
|
||||
FAN = 0x06
|
||||
};
|
||||
|
||||
enum class DataParameters : uint8_t {
|
||||
AC_POWER = 0x01,
|
||||
SET_POINT = 0x02,
|
||||
AC_MODE = 0x04,
|
||||
FAN_MODE = 0x05,
|
||||
USE_FAHRENHEIT = 0x07,
|
||||
TEN_DEGREE = 0x0A,
|
||||
HEALTH_MODE = 0x0B,
|
||||
BEEPER_STATUS = 0x16,
|
||||
LOCK_REMOTE = 0x17,
|
||||
QUIET_MODE = 0x19,
|
||||
FAST_MODE = 0x1A,
|
||||
};
|
||||
|
||||
enum class SpecialMode : uint8_t { NONE = 0x00, ELDERLY = 0x01, CHILDREN = 0x02, PREGNANT = 0x03 };
|
||||
|
||||
enum class FanMode : uint8_t { FAN_HIGH = 0x01, FAN_MID = 0x02, FAN_LOW = 0x03, FAN_AUTO = 0x05 };
|
||||
@@ -124,11 +138,7 @@ struct HaierPacketSensors {
|
||||
uint16_t co2_value; // CO2 value (0 PPM - 10000 PPM, 1 PPM step)
|
||||
};
|
||||
|
||||
struct HaierStatus {
|
||||
uint16_t subcommand;
|
||||
HaierPacketControl control;
|
||||
HaierPacketSensors sensors;
|
||||
};
|
||||
constexpr size_t HAIER_STATUS_FRAME_SIZE = 2 + sizeof(HaierPacketControl) + sizeof(HaierPacketSensors);
|
||||
|
||||
struct DeviceVersionAnswer {
|
||||
char protocol_version[8];
|
||||
@@ -140,76 +150,6 @@ struct DeviceVersionAnswer {
|
||||
uint8_t functions[2];
|
||||
};
|
||||
|
||||
// In this section comments:
|
||||
// - module is the ESP32 control module (communication module in Haier protocol document)
|
||||
// - device is the conditioner control board (network appliances in Haier protocol document)
|
||||
enum class FrameType : uint8_t {
|
||||
CONTROL = 0x01, // Requests or sets one or multiple parameters (module <-> device, required)
|
||||
STATUS = 0x02, // Contains one or multiple parameters values, usually answer to control frame (module <-> device,
|
||||
// required)
|
||||
INVALID = 0x03, // Communication error indication (module <-> device, required)
|
||||
ALARM_STATUS = 0x04, // Alarm status report (module <-> device, interactive, required)
|
||||
CONFIRM = 0x05, // Acknowledgment, usually used to confirm reception of frame if there is no special answer (module
|
||||
// <-> device, required)
|
||||
REPORT = 0x06, // Report frame (module <-> device, interactive, required)
|
||||
STOP_FAULT_ALARM = 0x09, // Stop fault alarm frame (module -> device, interactive, required)
|
||||
SYSTEM_DOWNLINK = 0x11, // System downlink frame (module -> device, optional)
|
||||
DEVICE_UPLINK = 0x12, // Device uplink frame (module <- device , interactive, optional)
|
||||
SYSTEM_QUERY = 0x13, // System query frame (module -> device, optional)
|
||||
SYSTEM_QUERY_RESPONSE = 0x14, // System query response frame (module <- device , optional)
|
||||
DEVICE_QUERY = 0x15, // Device query frame (module <- device, optional)
|
||||
DEVICE_QUERY_RESPONSE = 0x16, // Device query response frame (module -> device, optional)
|
||||
GROUP_COMMAND = 0x60, // Group command frame (module -> device, interactive, optional)
|
||||
GET_DEVICE_VERSION = 0x61, // Requests device version (module -> device, required)
|
||||
GET_DEVICE_VERSION_RESPONSE = 0x62, // Device version answer (module <- device, required_
|
||||
GET_ALL_ADDRESSES = 0x67, // Requests all devices addresses (module -> device, interactive, optional)
|
||||
GET_ALL_ADDRESSES_RESPONSE =
|
||||
0x68, // Answer to request of all devices addresses (module <- device , interactive, optional)
|
||||
HANDSET_CHANGE_NOTIFICATION = 0x69, // Handset change notification frame (module <- device , interactive, optional)
|
||||
GET_DEVICE_ID = 0x70, // Requests Device ID (module -> device, required)
|
||||
GET_DEVICE_ID_RESPONSE = 0x71, // Response to device ID request (module <- device , required)
|
||||
GET_ALARM_STATUS = 0x73, // Alarm status request (module -> device, required)
|
||||
GET_ALARM_STATUS_RESPONSE = 0x74, // Response to alarm status request (module <- device, required)
|
||||
GET_DEVICE_CONFIGURATION = 0x7C, // Requests device configuration (module -> device, interactive, required)
|
||||
GET_DEVICE_CONFIGURATION_RESPONSE =
|
||||
0x7D, // Response to device configuration request (module <- device, interactive, required)
|
||||
DOWNLINK_TRANSPARENT_TRANSMISSION = 0x8C, // Downlink transparent transmission (proxy data Haier cloud -> device)
|
||||
// (module -> device, interactive, optional)
|
||||
UPLINK_TRANSPARENT_TRANSMISSION = 0x8D, // Uplink transparent transmission (proxy data device -> Haier cloud) (module
|
||||
// <- device, interactive, optional)
|
||||
START_DEVICE_UPGRADE = 0xE1, // Initiate device OTA upgrade (module -> device, OTA required)
|
||||
START_DEVICE_UPGRADE_RESPONSE = 0xE2, // Response to initiate device upgrade command (module <- device, OTA required)
|
||||
GET_FIRMWARE_CONTENT = 0xE5, // Requests to send firmware (module <- device, OTA required)
|
||||
GET_FIRMWARE_CONTENT_RESPONSE =
|
||||
0xE6, // Response to send firmware request (module -> device, OTA required) (multipacket?)
|
||||
CHANGE_BAUD_RATE = 0xE7, // Requests to change port baud rate (module <- device, OTA required)
|
||||
CHANGE_BAUD_RATE_RESPONSE = 0xE8, // Response to change port baud rate request (module -> device, OTA required)
|
||||
GET_SUBBOARD_INFO = 0xE9, // Requests subboard information (module -> device, required)
|
||||
GET_SUBBOARD_INFO_RESPONSE = 0xEA, // Response to subboard information request (module <- device, required)
|
||||
GET_HARDWARE_INFO = 0xEB, // Requests information about device and subboard (module -> device, required)
|
||||
GET_HARDWARE_INFO_RESPONSE = 0xEC, // Response to hardware information request (module <- device, required)
|
||||
GET_UPGRADE_RESULT = 0xED, // Requests result of the firmware update (module <- device, OTA required)
|
||||
GET_UPGRADE_RESULT_RESPONSE = 0xEF, // Response to firmware update results request (module -> device, OTA required)
|
||||
GET_NETWORK_STATUS = 0xF0, // Requests network status (module <- device, interactive, optional)
|
||||
GET_NETWORK_STATUS_RESPONSE = 0xF1, // Response to network status request (module -> device, interactive, optional)
|
||||
START_WIFI_CONFIGURATION = 0xF2, // Starts WiFi configuration procedure (module <- device, interactive, required)
|
||||
START_WIFI_CONFIGURATION_RESPONSE =
|
||||
0xF3, // Response to start WiFi configuration request (module -> device, interactive, required)
|
||||
STOP_WIFI_CONFIGURATION = 0xF4, // Stop WiFi configuration procedure (module <- device, interactive, required)
|
||||
STOP_WIFI_CONFIGURATION_RESPONSE =
|
||||
0xF5, // Response to stop WiFi configuration request (module -> device, interactive, required)
|
||||
REPORT_NETWORK_STATUS = 0xF7, // Reports network status (module -> device, required)
|
||||
CLEAR_CONFIGURATION = 0xF8, // Request to clear module configuration (module <- device, interactive, optional)
|
||||
BIG_DATA_REPORT_CONFIGURATION =
|
||||
0xFA, // Configuration for autoreport device full status (module -> device, interactive, optional)
|
||||
BIG_DATA_REPORT_CONFIGURATION_RESPONSE =
|
||||
0xFB, // Response to set big data configuration (module <- device, interactive, optional)
|
||||
GET_MANAGEMENT_INFORMATION = 0xFC, // Request management information from device (module -> device, required)
|
||||
GET_MANAGEMENT_INFORMATION_RESPONSE =
|
||||
0xFD, // Response to management information request (module <- device, required)
|
||||
WAKE_UP = 0xFE, // Request to wake up (module <-> device, optional)
|
||||
};
|
||||
|
||||
enum class SubcommandsControl : uint16_t {
|
||||
GET_PARAMETERS = 0x4C01, // Request specific parameters (packet content: parameter ID1 + parameter ID2 + ...)
|
||||
GET_USER_DATA = 0x4D01, // Request all user data from device (packet content: None)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,27 +13,27 @@ class Smartair2Climate : public HaierClimateBase {
|
||||
Smartair2Climate &operator=(const Smartair2Climate &) = delete;
|
||||
~Smartair2Climate();
|
||||
void dump_config() override;
|
||||
void set_alternative_swing_control(bool swing_control);
|
||||
|
||||
protected:
|
||||
void set_handlers() override;
|
||||
void process_phase(std::chrono::steady_clock::time_point now) override;
|
||||
haier_protocol::HaierMessage get_power_message(bool state) override;
|
||||
haier_protocol::HaierMessage get_control_message() override;
|
||||
bool is_message_invalid(uint8_t message_type) override;
|
||||
void set_phase(HaierClimateBase::ProtocolPhases phase) override;
|
||||
// Answer and timeout handlers
|
||||
haier_protocol::HandlerError status_handler_(uint8_t request_type, uint8_t message_type, const uint8_t *data,
|
||||
// Answer handlers
|
||||
haier_protocol::HandlerError status_handler_(haier_protocol::FrameType request_type,
|
||||
haier_protocol::FrameType message_type, const uint8_t *data,
|
||||
size_t data_size);
|
||||
haier_protocol::HandlerError get_device_version_answer_handler_(uint8_t request_type, uint8_t message_type,
|
||||
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);
|
||||
haier_protocol::HandlerError get_device_id_answer_handler_(uint8_t request_type, uint8_t message_type,
|
||||
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);
|
||||
haier_protocol::HandlerError report_network_status_answer_handler_(uint8_t request_type, uint8_t message_type,
|
||||
const uint8_t *data, size_t data_size);
|
||||
haier_protocol::HandlerError initial_messages_timeout_handler_(uint8_t message_type);
|
||||
haier_protocol::HandlerError messages_timeout_handler_with_cycle_for_init_(haier_protocol::FrameType message_type);
|
||||
// Helper functions
|
||||
haier_protocol::HandlerError process_status_message_(const uint8_t *packet, uint8_t size);
|
||||
std::unique_ptr<uint8_t[]> last_status_message_;
|
||||
unsigned int timeouts_counter_;
|
||||
bool use_alternative_swing_control_;
|
||||
};
|
||||
|
||||
} // namespace haier
|
||||
|
||||
@@ -41,8 +41,9 @@ struct HaierPacketControl {
|
||||
// 24
|
||||
uint8_t : 8;
|
||||
// 25
|
||||
uint8_t swing_both; // If 1 - swing both direction, if 0 - horizontal_swing and vertical_swing define
|
||||
// vertical/horizontal/off
|
||||
uint8_t swing_mode; // In normal mode: If 1 - swing both direction, if 0 - horizontal_swing and
|
||||
// vertical_swing define vertical/horizontal/off
|
||||
// In alternative mode: 0 - off, 01 - vertical, 02 - horizontal, 03 - both
|
||||
// 26
|
||||
uint8_t : 3;
|
||||
uint8_t use_fahrenheit : 1;
|
||||
@@ -82,19 +83,6 @@ struct HaierStatus {
|
||||
HaierPacketControl control;
|
||||
};
|
||||
|
||||
enum class FrameType : uint8_t {
|
||||
CONTROL = 0x01,
|
||||
STATUS = 0x02,
|
||||
INVALID = 0x03,
|
||||
CONFIRM = 0x05,
|
||||
GET_DEVICE_VERSION = 0x61,
|
||||
GET_DEVICE_VERSION_RESPONSE = 0x62,
|
||||
GET_DEVICE_ID = 0x70,
|
||||
GET_DEVICE_ID_RESPONSE = 0x71,
|
||||
REPORT_NETWORK_STATUS = 0xF7,
|
||||
NO_COMMAND = 0xFF,
|
||||
};
|
||||
|
||||
} // namespace smartair2_protocol
|
||||
} // namespace haier
|
||||
} // namespace esphome
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ lib_deps =
|
||||
bblanchon/ArduinoJson@6.18.5 ; json
|
||||
wjtje/qr-code-generator-library@1.7.0 ; qr_code
|
||||
functionpointer/arduino-MLX90393@1.0.0 ; mlx90393
|
||||
pavlodn/HaierProtocol@0.9.20 ; haier
|
||||
pavlodn/HaierProtocol@0.9.24 ; haier
|
||||
; This is using the repository until a new release is published to PlatformIO
|
||||
https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library
|
||||
build_flags =
|
||||
|
||||
Reference in New Issue
Block a user