mirror of
https://github.com/esphome/esphome.git
synced 2024-11-28 01:34:18 +01:00
Haier climate integration update (#7416)
Co-authored-by: Pavlo Dudnytskyi <pdudnytskyi@astrata.eu>
This commit is contained in:
parent
d0dc275e30
commit
446f7e0a7e
17 changed files with 492 additions and 107 deletions
|
@ -166,6 +166,7 @@ esphome/components/haier/* @paveldn
|
||||||
esphome/components/haier/binary_sensor/* @paveldn
|
esphome/components/haier/binary_sensor/* @paveldn
|
||||||
esphome/components/haier/button/* @paveldn
|
esphome/components/haier/button/* @paveldn
|
||||||
esphome/components/haier/sensor/* @paveldn
|
esphome/components/haier/sensor/* @paveldn
|
||||||
|
esphome/components/haier/switch/* @paveldn
|
||||||
esphome/components/haier/text_sensor/* @paveldn
|
esphome/components/haier/text_sensor/* @paveldn
|
||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
esphome/components/hbridge/fan/* @WeekendWarrior
|
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||||
|
|
|
@ -114,7 +114,6 @@ SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = {
|
||||||
SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
|
SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = {
|
||||||
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
|
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
|
||||||
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
|
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
|
||||||
"ECO": ClimatePreset.CLIMATE_PRESET_ECO,
|
|
||||||
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
|
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +239,9 @@ CONFIG_SCHEMA = cv.All(
|
||||||
): cv.ensure_list(
|
): cv.ensure_list(
|
||||||
cv.enum(SUPPORTED_HON_CONTROL_METHODS, upper=True)
|
cv.enum(SUPPORTED_HON_CONTROL_METHODS, upper=True)
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_BEEPER, default=True): cv.boolean,
|
cv.Optional(CONF_BEEPER): cv.invalid(
|
||||||
|
f"The {CONF_BEEPER} option is deprecated, use beeper_on/beeper_off actions or beeper switch for a haier platform instead"
|
||||||
|
),
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE
|
CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE
|
||||||
): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50),
|
): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50),
|
||||||
|
@ -254,7 +255,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
): cv.int_range(min=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE),
|
): cv.int_range(min=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE),
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_SUPPORTED_PRESETS,
|
CONF_SUPPORTED_PRESETS,
|
||||||
default=["BOOST", "ECO", "SLEEP"], # No AWAY by default
|
default=["BOOST", "SLEEP"], # No AWAY by default
|
||||||
): cv.ensure_list(
|
): cv.ensure_list(
|
||||||
cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True)
|
cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True)
|
||||||
),
|
),
|
||||||
|
|
|
@ -52,8 +52,6 @@ bool check_timeout(std::chrono::steady_clock::time_point now, std::chrono::stead
|
||||||
HaierClimateBase::HaierClimateBase()
|
HaierClimateBase::HaierClimateBase()
|
||||||
: haier_protocol_(*this),
|
: haier_protocol_(*this),
|
||||||
protocol_phase_(ProtocolPhases::SENDING_INIT_1),
|
protocol_phase_(ProtocolPhases::SENDING_INIT_1),
|
||||||
display_status_(true),
|
|
||||||
health_mode_(false),
|
|
||||||
force_send_control_(false),
|
force_send_control_(false),
|
||||||
forced_request_status_(false),
|
forced_request_status_(false),
|
||||||
reset_protocol_request_(false),
|
reset_protocol_request_(false),
|
||||||
|
@ -127,21 +125,34 @@ haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool HaierClimateBase::get_display_state() const { return this->display_status_; }
|
void HaierClimateBase::save_settings() {
|
||||||
|
HaierBaseSettings settings{this->get_health_mode(), this->get_display_state()};
|
||||||
|
if (!this->base_rtc_.save(&settings)) {
|
||||||
|
ESP_LOGW(TAG, "Failed to save settings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HaierClimateBase::get_display_state() const {
|
||||||
|
return (this->display_status_ == SwitchState::ON) || (this->display_status_ == SwitchState::PENDING_ON);
|
||||||
|
}
|
||||||
|
|
||||||
void HaierClimateBase::set_display_state(bool state) {
|
void HaierClimateBase::set_display_state(bool state) {
|
||||||
if (this->display_status_ != state) {
|
if (state != this->get_display_state()) {
|
||||||
this->display_status_ = state;
|
this->display_status_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
|
this->save_settings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HaierClimateBase::get_health_mode() const { return this->health_mode_; }
|
bool HaierClimateBase::get_health_mode() const {
|
||||||
|
return (this->health_mode_ == SwitchState::ON) || (this->health_mode_ == SwitchState::PENDING_ON);
|
||||||
|
}
|
||||||
|
|
||||||
void HaierClimateBase::set_health_mode(bool state) {
|
void HaierClimateBase::set_health_mode(bool state) {
|
||||||
if (this->health_mode_ != state) {
|
if (state != this->get_health_mode()) {
|
||||||
this->health_mode_ = state;
|
this->health_mode_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
|
this->save_settings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,6 +298,14 @@ void HaierClimateBase::loop() {
|
||||||
}
|
}
|
||||||
this->process_phase(now);
|
this->process_phase(now);
|
||||||
this->haier_protocol_.loop();
|
this->haier_protocol_.loop();
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
if ((this->display_switch_ != nullptr) && (this->display_switch_->state != this->get_display_state())) {
|
||||||
|
this->display_switch_->publish_state(this->get_display_state());
|
||||||
|
}
|
||||||
|
if ((this->health_mode_switch_ != nullptr) && (this->health_mode_switch_->state != this->get_health_mode())) {
|
||||||
|
this->health_mode_switch_->publish_state(this->get_health_mode());
|
||||||
|
}
|
||||||
|
#endif // USE_SWITCH
|
||||||
}
|
}
|
||||||
|
|
||||||
void HaierClimateBase::process_protocol_reset() {
|
void HaierClimateBase::process_protocol_reset() {
|
||||||
|
@ -329,6 +348,26 @@ bool HaierClimateBase::prepare_pending_action() {
|
||||||
|
|
||||||
ClimateTraits HaierClimateBase::traits() { return traits_; }
|
ClimateTraits HaierClimateBase::traits() { return traits_; }
|
||||||
|
|
||||||
|
void HaierClimateBase::initialization() {
|
||||||
|
constexpr uint32_t restore_settings_version = 0xA77D21EF;
|
||||||
|
this->base_rtc_ =
|
||||||
|
global_preferences->make_preference<HaierBaseSettings>(this->get_object_id_hash() ^ restore_settings_version);
|
||||||
|
HaierBaseSettings recovered;
|
||||||
|
if (!this->base_rtc_.load(&recovered)) {
|
||||||
|
recovered = {false, true};
|
||||||
|
}
|
||||||
|
this->display_status_ = recovered.display_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
|
this->health_mode_ = recovered.health_mode ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
if (this->display_switch_ != nullptr) {
|
||||||
|
this->display_switch_->publish_state(this->get_display_state());
|
||||||
|
}
|
||||||
|
if (this->health_mode_switch_ != nullptr) {
|
||||||
|
this->health_mode_switch_->publish_state(this->get_health_mode());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void HaierClimateBase::control(const ClimateCall &call) {
|
void HaierClimateBase::control(const ClimateCall &call) {
|
||||||
ESP_LOGD("Control", "Control call");
|
ESP_LOGD("Control", "Control call");
|
||||||
if (!this->valid_connection()) {
|
if (!this->valid_connection()) {
|
||||||
|
@ -353,6 +392,22 @@ void HaierClimateBase::control(const ClimateCall &call) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
void HaierClimateBase::set_display_switch(switch_::Switch *sw) {
|
||||||
|
this->display_switch_ = sw;
|
||||||
|
if ((this->display_switch_ != nullptr) && (this->valid_connection())) {
|
||||||
|
this->display_switch_->publish_state(this->get_display_state());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HaierClimateBase::set_health_mode_switch(switch_::Switch *sw) {
|
||||||
|
this->health_mode_switch_ = sw;
|
||||||
|
if ((this->health_mode_switch_ != nullptr) && (this->valid_connection())) {
|
||||||
|
this->health_mode_switch_->publish_state(this->get_health_mode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void HaierClimateBase::HvacSettings::reset() {
|
void HaierClimateBase::HvacSettings::reset() {
|
||||||
this->valid = false;
|
this->valid = false;
|
||||||
this->mode.reset();
|
this->mode.reset();
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
// HaierProtocol
|
// HaierProtocol
|
||||||
#include <protocol/haier_protocol.h>
|
#include <protocol/haier_protocol.h>
|
||||||
|
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace haier {
|
namespace haier {
|
||||||
|
|
||||||
|
@ -20,10 +24,24 @@ enum class ActionRequest : uint8_t {
|
||||||
START_STERI_CLEAN = 5, // only hOn
|
START_STERI_CLEAN = 5, // only hOn
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct HaierBaseSettings {
|
||||||
|
bool health_mode;
|
||||||
|
bool display_state;
|
||||||
|
};
|
||||||
|
|
||||||
class HaierClimateBase : public esphome::Component,
|
class HaierClimateBase : public esphome::Component,
|
||||||
public esphome::climate::Climate,
|
public esphome::climate::Climate,
|
||||||
public esphome::uart::UARTDevice,
|
public esphome::uart::UARTDevice,
|
||||||
public haier_protocol::ProtocolStream {
|
public haier_protocol::ProtocolStream {
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
public:
|
||||||
|
void set_display_switch(switch_::Switch *sw);
|
||||||
|
void set_health_mode_switch(switch_::Switch *sw);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
switch_::Switch *display_switch_{nullptr};
|
||||||
|
switch_::Switch *health_mode_switch_{nullptr};
|
||||||
|
#endif
|
||||||
public:
|
public:
|
||||||
HaierClimateBase();
|
HaierClimateBase();
|
||||||
HaierClimateBase(const HaierClimateBase &) = delete;
|
HaierClimateBase(const HaierClimateBase &) = delete;
|
||||||
|
@ -82,7 +100,8 @@ class HaierClimateBase : public esphome::Component,
|
||||||
virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
|
virtual void process_phase(std::chrono::steady_clock::time_point now) = 0;
|
||||||
virtual haier_protocol::HaierMessage get_control_message() = 0; // NOLINT(readability-identifier-naming)
|
virtual haier_protocol::HaierMessage get_control_message() = 0; // NOLINT(readability-identifier-naming)
|
||||||
virtual haier_protocol::HaierMessage get_power_message(bool state) = 0; // NOLINT(readability-identifier-naming)
|
virtual haier_protocol::HaierMessage get_power_message(bool state) = 0; // NOLINT(readability-identifier-naming)
|
||||||
virtual void initialization(){};
|
virtual void save_settings();
|
||||||
|
virtual void initialization();
|
||||||
virtual bool prepare_pending_action();
|
virtual bool prepare_pending_action();
|
||||||
virtual void process_protocol_reset();
|
virtual void process_protocol_reset();
|
||||||
esphome::climate::ClimateTraits traits() override;
|
esphome::climate::ClimateTraits traits() override;
|
||||||
|
@ -127,13 +146,19 @@ class HaierClimateBase : public esphome::Component,
|
||||||
ActionRequest action;
|
ActionRequest action;
|
||||||
esphome::optional<haier_protocol::HaierMessage> message;
|
esphome::optional<haier_protocol::HaierMessage> message;
|
||||||
};
|
};
|
||||||
|
enum class SwitchState {
|
||||||
|
OFF = 0b00,
|
||||||
|
ON = 0b01,
|
||||||
|
PENDING_OFF = 0b10,
|
||||||
|
PENDING_ON = 0b11,
|
||||||
|
};
|
||||||
haier_protocol::ProtocolHandler haier_protocol_;
|
haier_protocol::ProtocolHandler haier_protocol_;
|
||||||
ProtocolPhases protocol_phase_;
|
ProtocolPhases protocol_phase_;
|
||||||
esphome::optional<PendingAction> action_request_;
|
esphome::optional<PendingAction> action_request_;
|
||||||
uint8_t fan_mode_speed_;
|
uint8_t fan_mode_speed_;
|
||||||
uint8_t other_modes_fan_speed_;
|
uint8_t other_modes_fan_speed_;
|
||||||
bool display_status_;
|
SwitchState display_status_{SwitchState::ON};
|
||||||
bool health_mode_;
|
SwitchState health_mode_{SwitchState::OFF};
|
||||||
bool force_send_control_;
|
bool force_send_control_;
|
||||||
bool forced_request_status_;
|
bool forced_request_status_;
|
||||||
bool reset_protocol_request_;
|
bool reset_protocol_request_;
|
||||||
|
@ -148,6 +173,7 @@ class HaierClimateBase : public esphome::Component,
|
||||||
std::chrono::steady_clock::time_point last_status_request_; // To request AC status
|
std::chrono::steady_clock::time_point last_status_request_; // To request AC status
|
||||||
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
|
||||||
CallbackManager<void(const char *, size_t)> status_message_callback_{};
|
CallbackManager<void(const char *, size_t)> status_message_callback_{};
|
||||||
|
ESPPreferenceObject base_rtc_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StatusMessageTrigger : public Trigger<const char *, size_t> {
|
class StatusMessageTrigger : public Trigger<const char *, size_t> {
|
||||||
|
|
|
@ -31,9 +31,32 @@ HonClimate::HonClimate()
|
||||||
|
|
||||||
HonClimate::~HonClimate() {}
|
HonClimate::~HonClimate() {}
|
||||||
|
|
||||||
void HonClimate::set_beeper_state(bool state) { this->beeper_status_ = state; }
|
void HonClimate::set_beeper_state(bool state) {
|
||||||
|
if (state != this->settings_.beeper_state) {
|
||||||
|
this->settings_.beeper_state = state;
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
this->beeper_switch_->publish_state(state);
|
||||||
|
#endif
|
||||||
|
this->hon_rtc_.save(&this->settings_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool HonClimate::get_beeper_state() const { return this->beeper_status_; }
|
bool HonClimate::get_beeper_state() const { return this->settings_.beeper_state; }
|
||||||
|
|
||||||
|
void HonClimate::set_quiet_mode_state(bool state) {
|
||||||
|
if (state != this->get_quiet_mode_state()) {
|
||||||
|
this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
|
this->settings_.quiet_mode_state = state;
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
this->quiet_mode_switch_->publish_state(state);
|
||||||
|
#endif
|
||||||
|
this->hon_rtc_.save(&this->settings_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HonClimate::get_quiet_mode_state() const {
|
||||||
|
return (this->quiet_mode_state_ == SwitchState::ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON);
|
||||||
|
}
|
||||||
|
|
||||||
esphome::optional<hon_protocol::VerticalSwingMode> HonClimate::get_vertical_airflow() const {
|
esphome::optional<hon_protocol::VerticalSwingMode> HonClimate::get_vertical_airflow() const {
|
||||||
return this->current_vertical_swing_;
|
return this->current_vertical_swing_;
|
||||||
|
@ -474,16 +497,19 @@ haier_protocol::HaierMessage HonClimate::get_power_message(bool state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HonClimate::initialization() {
|
void HonClimate::initialization() {
|
||||||
constexpr uint32_t restore_settings_version = 0xE834D8DCUL;
|
HaierClimateBase::initialization();
|
||||||
this->rtc_ = global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
|
constexpr uint32_t restore_settings_version = 0x57EB59DDUL;
|
||||||
|
this->hon_rtc_ =
|
||||||
|
global_preferences->make_preference<HonSettings>(this->get_object_id_hash() ^ restore_settings_version);
|
||||||
HonSettings recovered;
|
HonSettings recovered;
|
||||||
if (this->rtc_.load(&recovered)) {
|
if (this->hon_rtc_.load(&recovered)) {
|
||||||
this->settings_ = recovered;
|
this->settings_ = recovered;
|
||||||
} else {
|
} else {
|
||||||
this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER};
|
this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER, true, false};
|
||||||
}
|
}
|
||||||
this->current_vertical_swing_ = this->settings_.last_vertiacal_swing;
|
this->current_vertical_swing_ = this->settings_.last_vertiacal_swing;
|
||||||
this->current_horizontal_swing_ = this->settings_.last_horizontal_swing;
|
this->current_horizontal_swing_ = this->settings_.last_horizontal_swing;
|
||||||
|
this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
haier_protocol::HaierMessage HonClimate::get_control_message() {
|
haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
|
@ -519,8 +545,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
out_data->ac_power = 1;
|
out_data->ac_power = 1;
|
||||||
out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::FAN;
|
out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::FAN;
|
||||||
out_data->fan_mode = this->fan_mode_speed_; // Auto doesn't work in fan only mode
|
out_data->fan_mode = this->fan_mode_speed_; // Auto doesn't work in fan only mode
|
||||||
// Disabling boost and eco mode for Fan only
|
// Disabling boost for Fan only
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_MODE_COOL:
|
case CLIMATE_MODE_COOL:
|
||||||
|
@ -582,47 +607,34 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
}
|
}
|
||||||
if (out_data->ac_power == 0) {
|
if (out_data->ac_power == 0) {
|
||||||
// If AC is off - no presets allowed
|
// If AC is off - no presets allowed
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
} else if (climate_control.preset.has_value()) {
|
} else if (climate_control.preset.has_value()) {
|
||||||
switch (climate_control.preset.value()) {
|
switch (climate_control.preset.value()) {
|
||||||
case CLIMATE_PRESET_NONE:
|
case CLIMATE_PRESET_NONE:
|
||||||
out_data->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->fast_mode = 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
out_data->ten_degree = 0;
|
out_data->ten_degree = 0;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_BOOST:
|
case CLIMATE_PRESET_BOOST:
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
// Boost is not supported in Fan only mode
|
// Boost is not supported in Fan only mode
|
||||||
out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
|
out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
out_data->ten_degree = 0;
|
out_data->ten_degree = 0;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_AWAY:
|
case CLIMATE_PRESET_AWAY:
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
// 10 degrees allowed only in heat mode
|
// 10 degrees allowed only in heat mode
|
||||||
out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0;
|
out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_SLEEP:
|
case CLIMATE_PRESET_SLEEP:
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
out_data->sleep_mode = 1;
|
out_data->sleep_mode = 1;
|
||||||
out_data->ten_degree = 0;
|
out_data->ten_degree = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ESP_LOGE("Control", "Unsupported preset");
|
ESP_LOGE("Control", "Unsupported preset");
|
||||||
out_data->quiet_mode = 0;
|
|
||||||
out_data->fast_mode = 0;
|
out_data->fast_mode = 0;
|
||||||
out_data->sleep_mode = 0;
|
out_data->sleep_mode = 0;
|
||||||
out_data->ten_degree = 0;
|
out_data->ten_degree = 0;
|
||||||
|
@ -638,10 +650,23 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
|
||||||
out_data->horizontal_swing_mode = (uint8_t) this->pending_horizontal_direction_.value();
|
out_data->horizontal_swing_mode = (uint8_t) this->pending_horizontal_direction_.value();
|
||||||
this->pending_horizontal_direction_.reset();
|
this->pending_horizontal_direction_.reset();
|
||||||
}
|
}
|
||||||
out_data->beeper_status = ((!this->beeper_status_) || (!has_hvac_settings)) ? 1 : 0;
|
{
|
||||||
|
// Quiet mode
|
||||||
|
if ((out_data->ac_power == 0) || (out_data->ac_mode == (uint8_t) hon_protocol::ConditioningMode::FAN)) {
|
||||||
|
// If AC is off or in fan only mode - no quiet mode allowed
|
||||||
|
out_data->quiet_mode = 0;
|
||||||
|
} else {
|
||||||
|
out_data->quiet_mode = this->get_quiet_mode_state() ? 1 : 0;
|
||||||
|
}
|
||||||
|
// Clean quiet mode state pending flag
|
||||||
|
this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01);
|
||||||
|
}
|
||||||
|
out_data->beeper_status = ((!this->get_beeper_state()) || (!has_hvac_settings)) ? 1 : 0;
|
||||||
control_out_buffer[4] = 0; // This byte should be cleared before setting values
|
control_out_buffer[4] = 0; // This byte should be cleared before setting values
|
||||||
out_data->display_status = this->display_status_ ? 1 : 0;
|
out_data->display_status = this->get_display_state() ? 1 : 0;
|
||||||
out_data->health_mode = this->health_mode_ ? 1 : 0;
|
this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01);
|
||||||
|
out_data->health_mode = this->get_health_mode() ? 1 : 0;
|
||||||
|
this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
|
||||||
return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
||||||
(uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
|
(uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS,
|
||||||
control_out_buffer, this->real_control_packet_size_);
|
control_out_buffer, this->real_control_packet_size_);
|
||||||
|
@ -765,6 +790,22 @@ void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::stri
|
||||||
}
|
}
|
||||||
#endif // USE_TEXT_SENSOR
|
#endif // USE_TEXT_SENSOR
|
||||||
|
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
void HonClimate::set_beeper_switch(switch_::Switch *sw) {
|
||||||
|
this->beeper_switch_ = sw;
|
||||||
|
if (this->beeper_switch_ != nullptr) {
|
||||||
|
this->beeper_switch_->publish_state(this->get_beeper_state());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HonClimate::set_quiet_mode_switch(switch_::Switch *sw) {
|
||||||
|
this->quiet_mode_switch_ = sw;
|
||||||
|
if (this->quiet_mode_switch_ != nullptr) {
|
||||||
|
this->quiet_mode_switch_->publish_state(this->settings_.quiet_mode_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // USE_SWITCH
|
||||||
|
|
||||||
haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
|
haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) {
|
||||||
size_t expected_size =
|
size_t expected_size =
|
||||||
2 + this->status_message_header_size_ + this->real_control_packet_size_ + this->real_sensors_packet_size_;
|
2 + this->status_message_header_size_ + this->real_control_packet_size_ + this->real_sensors_packet_size_;
|
||||||
|
@ -827,9 +868,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
{
|
{
|
||||||
// Extra modes/presets
|
// Extra modes/presets
|
||||||
optional<ClimatePreset> old_preset = this->preset;
|
optional<ClimatePreset> old_preset = this->preset;
|
||||||
if (packet.control.quiet_mode != 0) {
|
if (packet.control.fast_mode != 0) {
|
||||||
this->preset = CLIMATE_PRESET_ECO;
|
|
||||||
} else if (packet.control.fast_mode != 0) {
|
|
||||||
this->preset = CLIMATE_PRESET_BOOST;
|
this->preset = CLIMATE_PRESET_BOOST;
|
||||||
} else if (packet.control.sleep_mode != 0) {
|
} else if (packet.control.sleep_mode != 0) {
|
||||||
this->preset = CLIMATE_PRESET_SLEEP;
|
this->preset = CLIMATE_PRESET_SLEEP;
|
||||||
|
@ -883,28 +922,26 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
}
|
}
|
||||||
should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
|
should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
|
||||||
}
|
}
|
||||||
{
|
|
||||||
// Display status
|
// Display status
|
||||||
// should be before "Climate mode" because it is changing this->mode
|
// should be before "Climate mode" because it is changing this->mode
|
||||||
if (packet.control.ac_power != 0) {
|
if (packet.control.ac_power != 0) {
|
||||||
// if AC is off display status always ON so process it only when AC is on
|
// if AC is off display status always ON so process it only when AC is on
|
||||||
bool disp_status = packet.control.display_status != 0;
|
bool disp_status = packet.control.display_status != 0;
|
||||||
if (disp_status != this->display_status_) {
|
if (disp_status != this->get_display_state()) {
|
||||||
// Do something only if display status changed
|
// Do something only if display status changed
|
||||||
if (this->mode == CLIMATE_MODE_OFF) {
|
if (this->mode == CLIMATE_MODE_OFF) {
|
||||||
// AC just turned on from remote need to turn off display
|
// AC just turned on from remote need to turn off display
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
} else {
|
} else if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||||
this->display_status_ = disp_status;
|
this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
{
|
|
||||||
// Health mode
|
// Health mode
|
||||||
bool old_health_mode = this->health_mode_;
|
if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||||
this->health_mode_ = packet.control.health_mode == 1;
|
bool old_health_mode = this->get_health_mode();
|
||||||
should_publish = should_publish || (old_health_mode != this->health_mode_);
|
this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
|
||||||
|
should_publish = should_publish || (old_health_mode != this->get_health_mode());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
CleaningState new_cleaning;
|
CleaningState new_cleaning;
|
||||||
|
@ -958,17 +995,36 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
}
|
}
|
||||||
should_publish = should_publish || (old_mode != this->mode);
|
should_publish = should_publish || (old_mode != this->mode);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// Quiet mode, should be after climate mode
|
||||||
|
if ((this->mode != CLIMATE_MODE_FAN_ONLY) && (this->mode != CLIMATE_MODE_OFF) &&
|
||||||
|
((((uint8_t) this->quiet_mode_state_) & 0b10) == 0)) {
|
||||||
|
// In proper mode and not in pending state
|
||||||
|
bool new_quiet_mode = packet.control.quiet_mode != 0;
|
||||||
|
if (new_quiet_mode != this->get_quiet_mode_state()) {
|
||||||
|
this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF;
|
||||||
|
this->settings_.quiet_mode_state = new_quiet_mode;
|
||||||
|
this->hon_rtc_.save(&this->settings_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
// Swing mode
|
// Swing mode
|
||||||
ClimateSwingMode old_swing_mode = this->swing_mode;
|
ClimateSwingMode old_swing_mode = this->swing_mode;
|
||||||
if (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO) {
|
const std::set<ClimateSwingMode> &swing_modes = traits_.get_supported_swing_modes();
|
||||||
if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
|
bool vertical_swing_supported = swing_modes.find(CLIMATE_SWING_VERTICAL) != swing_modes.end();
|
||||||
|
bool horizontal_swing_supported = swing_modes.find(CLIMATE_SWING_HORIZONTAL) != swing_modes.end();
|
||||||
|
if (horizontal_swing_supported &&
|
||||||
|
(packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO)) {
|
||||||
|
if (vertical_swing_supported &&
|
||||||
|
(packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) {
|
||||||
this->swing_mode = CLIMATE_SWING_BOTH;
|
this->swing_mode = CLIMATE_SWING_BOTH;
|
||||||
} else {
|
} else {
|
||||||
this->swing_mode = CLIMATE_SWING_HORIZONTAL;
|
this->swing_mode = CLIMATE_SWING_HORIZONTAL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) {
|
if (vertical_swing_supported &&
|
||||||
|
(packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) {
|
||||||
this->swing_mode = CLIMATE_SWING_VERTICAL;
|
this->swing_mode = CLIMATE_SWING_VERTICAL;
|
||||||
} else {
|
} else {
|
||||||
this->swing_mode = CLIMATE_SWING_OFF;
|
this->swing_mode = CLIMATE_SWING_OFF;
|
||||||
|
@ -985,7 +1041,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
|
||||||
if (save_settings) {
|
if (save_settings) {
|
||||||
this->settings_.last_vertiacal_swing = this->current_vertical_swing_.value();
|
this->settings_.last_vertiacal_swing = this->current_vertical_swing_.value();
|
||||||
this->settings_.last_horizontal_swing = this->current_horizontal_swing_.value();
|
this->settings_.last_horizontal_swing = this->current_horizontal_swing_.value();
|
||||||
this->rtc_.save(&this->settings_);
|
this->hon_rtc_.save(&this->settings_);
|
||||||
}
|
}
|
||||||
should_publish = should_publish || (old_swing_mode != this->swing_mode);
|
should_publish = should_publish || (old_swing_mode != this->swing_mode);
|
||||||
}
|
}
|
||||||
|
@ -1017,7 +1073,7 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
||||||
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
||||||
(uint8_t) hon_protocol::DataParameters::BEEPER_STATUS,
|
(uint8_t) hon_protocol::DataParameters::BEEPER_STATUS,
|
||||||
this->beeper_status_ ? ZERO_BUF : ONE_BUF, 2));
|
this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2));
|
||||||
}
|
}
|
||||||
// Health mode
|
// Health mode
|
||||||
{
|
{
|
||||||
|
@ -1025,13 +1081,16 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
||||||
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
||||||
(uint8_t) hon_protocol::DataParameters::HEALTH_MODE,
|
(uint8_t) hon_protocol::DataParameters::HEALTH_MODE,
|
||||||
this->health_mode_ ? ONE_BUF : ZERO_BUF, 2));
|
this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2));
|
||||||
|
this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
|
||||||
}
|
}
|
||||||
// Climate mode
|
// Climate mode
|
||||||
|
ClimateMode climate_mode = this->mode;
|
||||||
bool new_power = this->mode != CLIMATE_MODE_OFF;
|
bool new_power = this->mode != CLIMATE_MODE_OFF;
|
||||||
uint8_t fan_mode_buf[] = {0x00, 0xFF};
|
uint8_t fan_mode_buf[] = {0x00, 0xFF};
|
||||||
uint8_t quiet_mode_buf[] = {0x00, 0xFF};
|
uint8_t quiet_mode_buf[] = {0x00, 0xFF};
|
||||||
if (climate_control.mode.has_value()) {
|
if (climate_control.mode.has_value()) {
|
||||||
|
climate_mode = climate_control.mode.value();
|
||||||
uint8_t buffer[2] = {0x00, 0x00};
|
uint8_t buffer[2] = {0x00, 0x00};
|
||||||
switch (climate_control.mode.value()) {
|
switch (climate_control.mode.value()) {
|
||||||
case CLIMATE_MODE_OFF:
|
case CLIMATE_MODE_OFF:
|
||||||
|
@ -1076,8 +1135,6 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
(uint8_t) hon_protocol::DataParameters::AC_MODE,
|
(uint8_t) hon_protocol::DataParameters::AC_MODE,
|
||||||
buffer, 2));
|
buffer, 2));
|
||||||
fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode
|
fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode
|
||||||
// Disabling eco mode for Fan only
|
|
||||||
quiet_mode_buf[1] = 0;
|
|
||||||
break;
|
break;
|
||||||
case CLIMATE_MODE_COOL:
|
case CLIMATE_MODE_COOL:
|
||||||
new_power = true;
|
new_power = true;
|
||||||
|
@ -1108,30 +1165,20 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
uint8_t away_mode_buf[] = {0x00, 0xFF};
|
uint8_t away_mode_buf[] = {0x00, 0xFF};
|
||||||
if (!new_power) {
|
if (!new_power) {
|
||||||
// If AC is off - no presets allowed
|
// If AC is off - no presets allowed
|
||||||
quiet_mode_buf[1] = 0x00;
|
|
||||||
fast_mode_buf[1] = 0x00;
|
fast_mode_buf[1] = 0x00;
|
||||||
away_mode_buf[1] = 0x00;
|
away_mode_buf[1] = 0x00;
|
||||||
} else if (climate_control.preset.has_value()) {
|
} else if (climate_control.preset.has_value()) {
|
||||||
switch (climate_control.preset.value()) {
|
switch (climate_control.preset.value()) {
|
||||||
case CLIMATE_PRESET_NONE:
|
case CLIMATE_PRESET_NONE:
|
||||||
quiet_mode_buf[1] = 0x00;
|
|
||||||
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;
|
fast_mode_buf[1] = 0x00;
|
||||||
away_mode_buf[1] = 0x00;
|
away_mode_buf[1] = 0x00;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_BOOST:
|
case CLIMATE_PRESET_BOOST:
|
||||||
quiet_mode_buf[1] = 0x00;
|
|
||||||
// Boost is not supported in Fan only mode
|
// Boost is not supported in Fan only mode
|
||||||
fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
|
fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00;
|
||||||
away_mode_buf[1] = 0x00;
|
away_mode_buf[1] = 0x00;
|
||||||
break;
|
break;
|
||||||
case CLIMATE_PRESET_AWAY:
|
case CLIMATE_PRESET_AWAY:
|
||||||
quiet_mode_buf[1] = 0x00;
|
|
||||||
fast_mode_buf[1] = 0x00;
|
fast_mode_buf[1] = 0x00;
|
||||||
away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00;
|
away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00;
|
||||||
break;
|
break;
|
||||||
|
@ -1140,8 +1187,18 @@ void HonClimate::fill_control_messages_queue_() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// Quiet mode
|
||||||
|
if (new_power && (climate_mode != CLIMATE_MODE_FAN_ONLY) && this->get_quiet_mode_state()) {
|
||||||
|
quiet_mode_buf[1] = 0x01;
|
||||||
|
} else {
|
||||||
|
quiet_mode_buf[1] = 0x00;
|
||||||
|
}
|
||||||
|
// Clean quiet mode state pending flag
|
||||||
|
this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01);
|
||||||
|
}
|
||||||
auto presets = this->traits_.get_supported_presets();
|
auto presets = this->traits_.get_supported_presets();
|
||||||
if ((quiet_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_ECO) != presets.end()))) {
|
if (quiet_mode_buf[1] != 0xFF) {
|
||||||
this->control_messages_queue_.push(
|
this->control_messages_queue_.push(
|
||||||
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL,
|
||||||
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
#include "esphome/components/text_sensor/text_sensor.h"
|
#include "esphome/components/text_sensor/text_sensor.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#endif
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "haier_base.h"
|
#include "haier_base.h"
|
||||||
#include "hon_packet.h"
|
#include "hon_packet.h"
|
||||||
|
@ -28,6 +31,8 @@ enum class HonControlMethod { MONITOR_ONLY = 0, SET_GROUP_PARAMETERS, SET_SINGLE
|
||||||
struct HonSettings {
|
struct HonSettings {
|
||||||
hon_protocol::VerticalSwingMode last_vertiacal_swing;
|
hon_protocol::VerticalSwingMode last_vertiacal_swing;
|
||||||
hon_protocol::HorizontalSwingMode last_horizontal_swing;
|
hon_protocol::HorizontalSwingMode last_horizontal_swing;
|
||||||
|
bool beeper_state;
|
||||||
|
bool quiet_mode_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HonClimate : public HaierClimateBase {
|
class HonClimate : public HaierClimateBase {
|
||||||
|
@ -86,6 +91,15 @@ class HonClimate : public HaierClimateBase {
|
||||||
protected:
|
protected:
|
||||||
void update_sub_text_sensor_(SubTextSensorType type, const std::string &value);
|
void update_sub_text_sensor_(SubTextSensorType type, const std::string &value);
|
||||||
text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr};
|
text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
public:
|
||||||
|
void set_beeper_switch(switch_::Switch *sw);
|
||||||
|
void set_quiet_mode_switch(switch_::Switch *sw);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
switch_::Switch *beeper_switch_{nullptr};
|
||||||
|
switch_::Switch *quiet_mode_switch_{nullptr};
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
HonClimate();
|
HonClimate();
|
||||||
|
@ -95,6 +109,8 @@ class HonClimate : public HaierClimateBase {
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void set_beeper_state(bool state);
|
void set_beeper_state(bool state);
|
||||||
bool get_beeper_state() const;
|
bool get_beeper_state() const;
|
||||||
|
void set_quiet_mode_state(bool state);
|
||||||
|
bool get_quiet_mode_state() const;
|
||||||
esphome::optional<hon_protocol::VerticalSwingMode> get_vertical_airflow() const;
|
esphome::optional<hon_protocol::VerticalSwingMode> get_vertical_airflow() const;
|
||||||
void set_vertical_airflow(hon_protocol::VerticalSwingMode direction);
|
void set_vertical_airflow(hon_protocol::VerticalSwingMode direction);
|
||||||
esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const;
|
esphome::optional<hon_protocol::HorizontalSwingMode> get_horizontal_airflow() const;
|
||||||
|
@ -153,7 +169,6 @@ class HonClimate : public HaierClimateBase {
|
||||||
bool functions_[5];
|
bool functions_[5];
|
||||||
};
|
};
|
||||||
|
|
||||||
bool beeper_status_;
|
|
||||||
CleaningState cleaning_status_;
|
CleaningState cleaning_status_;
|
||||||
bool got_valid_outdoor_temp_;
|
bool got_valid_outdoor_temp_;
|
||||||
esphome::optional<hon_protocol::VerticalSwingMode> pending_vertical_direction_{};
|
esphome::optional<hon_protocol::VerticalSwingMode> pending_vertical_direction_{};
|
||||||
|
@ -175,7 +190,8 @@ class HonClimate : public HaierClimateBase {
|
||||||
esphome::optional<hon_protocol::VerticalSwingMode> current_vertical_swing_{};
|
esphome::optional<hon_protocol::VerticalSwingMode> current_vertical_swing_{};
|
||||||
esphome::optional<hon_protocol::HorizontalSwingMode> current_horizontal_swing_{};
|
esphome::optional<hon_protocol::HorizontalSwingMode> current_horizontal_swing_{};
|
||||||
HonSettings settings_;
|
HonSettings settings_;
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject hon_rtc_;
|
||||||
|
SwitchState quiet_mode_state_{SwitchState::OFF};
|
||||||
};
|
};
|
||||||
|
|
||||||
class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> {
|
class HaierAlarmStartTrigger : public Trigger<uint8_t, const char *> {
|
||||||
|
|
|
@ -376,8 +376,10 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out_data->display_status = this->display_status_ ? 0 : 1;
|
out_data->display_status = this->get_display_state() ? 0 : 1;
|
||||||
out_data->health_mode = this->health_mode_ ? 1 : 0;
|
this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01);
|
||||||
|
out_data->health_mode = this->get_health_mode() ? 1 : 0;
|
||||||
|
this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01);
|
||||||
return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, 0x4D5F, control_out_buffer,
|
return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, 0x4D5F, control_out_buffer,
|
||||||
sizeof(smartair2_protocol::HaierPacketControl));
|
sizeof(smartair2_protocol::HaierPacketControl));
|
||||||
}
|
}
|
||||||
|
@ -446,28 +448,26 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin
|
||||||
}
|
}
|
||||||
should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
|
should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value());
|
||||||
}
|
}
|
||||||
{
|
|
||||||
// Display status
|
// Display status
|
||||||
// should be before "Climate mode" because it is changing this->mode
|
// should be before "Climate mode" because it is changing this->mode
|
||||||
if (packet.control.ac_power != 0) {
|
if (packet.control.ac_power != 0) {
|
||||||
// if AC is off display status always ON so process it only when AC is on
|
// if AC is off display status always ON so process it only when AC is on
|
||||||
bool disp_status = packet.control.display_status == 0;
|
bool disp_status = packet.control.display_status == 0;
|
||||||
if (disp_status != this->display_status_) {
|
if (disp_status != this->get_display_state()) {
|
||||||
// Do something only if display status changed
|
// Do something only if display status changed
|
||||||
if (this->mode == CLIMATE_MODE_OFF) {
|
if (this->mode == CLIMATE_MODE_OFF) {
|
||||||
// AC just turned on from remote need to turn off display
|
// AC just turned on from remote need to turn off display
|
||||||
this->force_send_control_ = true;
|
this->force_send_control_ = true;
|
||||||
} else {
|
} else if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||||
this->display_status_ = disp_status;
|
this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
{
|
|
||||||
// Health mode
|
// Health mode
|
||||||
bool old_health_mode = this->health_mode_;
|
if ((((uint8_t) this->health_mode_) & 0b10) == 0) {
|
||||||
this->health_mode_ = packet.control.health_mode == 1;
|
bool old_health_mode = this->get_health_mode();
|
||||||
should_publish = should_publish || (old_health_mode != this->health_mode_);
|
this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF;
|
||||||
|
should_publish = should_publish || (old_health_mode != this->get_health_mode());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// Climate mode
|
// Climate mode
|
||||||
|
|
91
esphome/components/haier/switch/__init__.py
Normal file
91
esphome/components/haier/switch/__init__.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
import esphome.final_validate as fv
|
||||||
|
from esphome.components import switch
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_BEEPER,
|
||||||
|
CONF_DISPLAY,
|
||||||
|
ENTITY_CATEGORY_CONFIG,
|
||||||
|
)
|
||||||
|
from ..climate import (
|
||||||
|
CONF_HAIER_ID,
|
||||||
|
CONF_PROTOCOL,
|
||||||
|
HaierClimateBase,
|
||||||
|
haier_ns,
|
||||||
|
PROTOCOL_HON,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@paveldn"]
|
||||||
|
BeeperSwitch = haier_ns.class_("BeeperSwitch", switch.Switch)
|
||||||
|
HealthModeSwitch = haier_ns.class_("HealthModeSwitch", switch.Switch)
|
||||||
|
DisplaySwitch = haier_ns.class_("DisplaySwitch", switch.Switch)
|
||||||
|
QuietModeSwitch = haier_ns.class_("QuietModeSwitch", switch.Switch)
|
||||||
|
|
||||||
|
# Haier switches
|
||||||
|
CONF_HEALTH_MODE = "health_mode"
|
||||||
|
CONF_QUIET_MODE = "quiet_mode"
|
||||||
|
|
||||||
|
# Additional icons
|
||||||
|
ICON_LEAF = "mdi:leaf"
|
||||||
|
ICON_LED_ON = "mdi:led-on"
|
||||||
|
ICON_VOLUME_HIGH = "mdi:volume-high"
|
||||||
|
ICON_VOLUME_OFF = "mdi:volume-off"
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_HAIER_ID): cv.use_id(HaierClimateBase),
|
||||||
|
cv.Optional(CONF_DISPLAY): switch.switch_schema(
|
||||||
|
DisplaySwitch,
|
||||||
|
icon=ICON_LED_ON,
|
||||||
|
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||||
|
default_restore_mode="DISABLED",
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HEALTH_MODE): switch.switch_schema(
|
||||||
|
HealthModeSwitch,
|
||||||
|
icon=ICON_LEAF,
|
||||||
|
default_restore_mode="DISABLED",
|
||||||
|
),
|
||||||
|
# Beeper switch is only supported for HonClimate
|
||||||
|
cv.Optional(CONF_BEEPER): switch.switch_schema(
|
||||||
|
BeeperSwitch,
|
||||||
|
icon=ICON_VOLUME_HIGH,
|
||||||
|
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||||
|
default_restore_mode="DISABLED",
|
||||||
|
),
|
||||||
|
# Quiet mode is only supported for HonClimate
|
||||||
|
cv.Optional(CONF_QUIET_MODE): switch.switch_schema(
|
||||||
|
QuietModeSwitch,
|
||||||
|
icon=ICON_VOLUME_OFF,
|
||||||
|
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||||
|
default_restore_mode="DISABLED",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _final_validate(config):
|
||||||
|
full_config = fv.full_config.get()
|
||||||
|
for switch_type in [CONF_BEEPER, CONF_QUIET_MODE]:
|
||||||
|
# Check switches that are only supported for HonClimate
|
||||||
|
if config.get(switch_type):
|
||||||
|
climate_path = full_config.get_path_for_id(config[CONF_HAIER_ID])[:-1]
|
||||||
|
climate_conf = full_config.get_config_for_path(climate_path)
|
||||||
|
protocol_type = climate_conf.get(CONF_PROTOCOL)
|
||||||
|
if protocol_type.casefold() != PROTOCOL_HON.casefold():
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"{switch_type} switch is only supported for hon climate"
|
||||||
|
)
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
parent = await cg.get_variable(config[CONF_HAIER_ID])
|
||||||
|
|
||||||
|
for switch_type in [CONF_DISPLAY, CONF_HEALTH_MODE, CONF_BEEPER, CONF_QUIET_MODE]:
|
||||||
|
if conf := config.get(switch_type):
|
||||||
|
sw_var = await switch.new_switch(conf)
|
||||||
|
await cg.register_parented(sw_var, parent)
|
||||||
|
cg.add(getattr(parent, f"set_{switch_type}_switch")(sw_var))
|
14
esphome/components/haier/switch/beeper.cpp
Normal file
14
esphome/components/haier/switch/beeper.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "beeper.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void BeeperSwitch::write_state(bool state) {
|
||||||
|
if (this->parent_->get_beeper_state() != state) {
|
||||||
|
this->parent_->set_beeper_state(state);
|
||||||
|
}
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/switch/beeper.h
Normal file
18
esphome/components/haier/switch/beeper.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#include "../hon_climate.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class BeeperSwitch : public switch_::Switch, public Parented<HonClimate> {
|
||||||
|
public:
|
||||||
|
BeeperSwitch() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
14
esphome/components/haier/switch/display.cpp
Normal file
14
esphome/components/haier/switch/display.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "display.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void DisplaySwitch::write_state(bool state) {
|
||||||
|
if (this->parent_->get_display_state() != state) {
|
||||||
|
this->parent_->set_display_state(state);
|
||||||
|
}
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/switch/display.h
Normal file
18
esphome/components/haier/switch/display.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#include "../haier_base.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class DisplaySwitch : public switch_::Switch, public Parented<HaierClimateBase> {
|
||||||
|
public:
|
||||||
|
DisplaySwitch() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
14
esphome/components/haier/switch/health_mode.cpp
Normal file
14
esphome/components/haier/switch/health_mode.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "health_mode.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void HealthModeSwitch::write_state(bool state) {
|
||||||
|
if (this->parent_->get_health_mode() != state) {
|
||||||
|
this->parent_->set_health_mode(state);
|
||||||
|
}
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/switch/health_mode.h
Normal file
18
esphome/components/haier/switch/health_mode.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#include "../haier_base.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class HealthModeSwitch : public switch_::Switch, public Parented<HaierClimateBase> {
|
||||||
|
public:
|
||||||
|
HealthModeSwitch() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
14
esphome/components/haier/switch/quiet_mode.cpp
Normal file
14
esphome/components/haier/switch/quiet_mode.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "quiet_mode.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
void QuietModeSwitch::write_state(bool state) {
|
||||||
|
if (this->parent_->get_quiet_mode_state() != state) {
|
||||||
|
this->parent_->set_quiet_mode_state(state);
|
||||||
|
}
|
||||||
|
this->publish_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
18
esphome/components/haier/switch/quiet_mode.h
Normal file
18
esphome/components/haier/switch/quiet_mode.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/switch/switch.h"
|
||||||
|
#include "../hon_climate.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace haier {
|
||||||
|
|
||||||
|
class QuietModeSwitch : public switch_::Switch, public Parented<HonClimate> {
|
||||||
|
public:
|
||||||
|
QuietModeSwitch() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace haier
|
||||||
|
} // namespace esphome
|
|
@ -16,7 +16,6 @@ climate:
|
||||||
name: Haier AC
|
name: Haier AC
|
||||||
wifi_signal: true
|
wifi_signal: true
|
||||||
answer_timeout: 200ms
|
answer_timeout: 200ms
|
||||||
beeper: true
|
|
||||||
visual:
|
visual:
|
||||||
min_temperature: 16 °C
|
min_temperature: 16 °C
|
||||||
max_temperature: 30 °C
|
max_temperature: 30 °C
|
||||||
|
@ -38,7 +37,6 @@ climate:
|
||||||
supported_presets:
|
supported_presets:
|
||||||
- AWAY
|
- AWAY
|
||||||
- BOOST
|
- BOOST
|
||||||
- ECO
|
|
||||||
- SLEEP
|
- SLEEP
|
||||||
on_alarm_start:
|
on_alarm_start:
|
||||||
then:
|
then:
|
||||||
|
@ -112,3 +110,15 @@ text_sensor:
|
||||||
name: Haier cleaning status
|
name: Haier cleaning status
|
||||||
protocol_version:
|
protocol_version:
|
||||||
name: Haier protocol version
|
name: Haier protocol version
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: haier
|
||||||
|
haier_id: haier_ac
|
||||||
|
beeper:
|
||||||
|
name: Haier beeper
|
||||||
|
display:
|
||||||
|
name: Haier display
|
||||||
|
health_mode:
|
||||||
|
name: Haier health mode
|
||||||
|
quiet_mode:
|
||||||
|
name: Haier quiet mode
|
||||||
|
|
Loading…
Reference in a new issue