Add preset, custom_preset and custom_fan_mode support to climate (#1471)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Lumpusz 2021-06-04 12:04:54 +02:00 committed by GitHub
parent 7bc51582f0
commit ebadaa9660
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 931 additions and 96 deletions

View file

@ -672,11 +672,12 @@ message CameraImageRequest {
// ==================== CLIMATE ====================
enum ClimateMode {
CLIMATE_MODE_OFF = 0;
CLIMATE_MODE_AUTO = 1;
CLIMATE_MODE_HEAT_COOL = 1;
CLIMATE_MODE_COOL = 2;
CLIMATE_MODE_HEAT = 3;
CLIMATE_MODE_FAN_ONLY = 4;
CLIMATE_MODE_DRY = 5;
CLIMATE_MODE_AUTO = 6;
}
enum ClimateFanMode {
CLIMATE_FAN_ON = 0;
@ -704,6 +705,15 @@ enum ClimateAction {
CLIMATE_ACTION_DRYING = 5;
CLIMATE_ACTION_FAN = 6;
}
enum ClimatePreset {
CLIMATE_PRESET_ECO = 0;
CLIMATE_PRESET_AWAY = 1;
CLIMATE_PRESET_BOOST = 2;
CLIMATE_PRESET_COMFORT = 3;
CLIMATE_PRESET_HOME = 4;
CLIMATE_PRESET_SLEEP = 5;
CLIMATE_PRESET_ACTIVITY = 6;
}
message ListEntitiesClimateResponse {
option (id) = 46;
option (source) = SOURCE_SERVER;
@ -724,6 +734,9 @@ message ListEntitiesClimateResponse {
bool supports_action = 12;
repeated ClimateFanMode supported_fan_modes = 13;
repeated ClimateSwingMode supported_swing_modes = 14;
repeated string supported_custom_fan_modes = 15;
repeated ClimatePreset supported_presets = 16;
repeated string supported_custom_presets = 17;
}
message ClimateStateResponse {
option (id) = 47;
@ -741,6 +754,9 @@ message ClimateStateResponse {
ClimateAction action = 8;
ClimateFanMode fan_mode = 9;
ClimateSwingMode swing_mode = 10;
string custom_fan_mode = 11;
ClimatePreset preset = 12;
string custom_preset = 13;
}
message ClimateCommandRequest {
option (id) = 48;
@ -763,4 +779,10 @@ message ClimateCommandRequest {
ClimateFanMode fan_mode = 13;
bool has_swing_mode = 14;
ClimateSwingMode swing_mode = 15;
bool has_custom_fan_mode = 16;
string custom_fan_mode = 17;
bool has_preset = 18;
ClimatePreset preset = 19;
bool has_custom_preset = 20;
string custom_preset = 21;
}

View file

@ -477,8 +477,14 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
}
if (traits.get_supports_away())
resp.away = climate->away;
if (traits.get_supports_fan_modes())
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode);
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
resp.custom_fan_mode = climate->custom_fan_mode.value();
if (traits.get_supports_presets() && climate->preset.has_value())
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
resp.custom_preset = climate->custom_preset.value();
if (traits.get_supports_swing_modes())
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
return this->send_climate_state_response(resp);
@ -492,8 +498,9 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
msg.unique_id = get_default_unique_id("climate", climate);
msg.supports_current_temperature = traits.get_supports_current_temperature();
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) {
for (auto mode :
{climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL, climate::CLIMATE_MODE_HEAT,
climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY, climate::CLIMATE_MODE_HEAT_COOL}) {
if (traits.supports_mode(mode))
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
}
@ -508,6 +515,18 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
if (traits.supports_fan_mode(fan_mode))
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
}
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes()) {
msg.supported_custom_fan_modes.push_back(custom_fan_mode);
}
for (auto preset : {climate::CLIMATE_PRESET_ECO, climate::CLIMATE_PRESET_AWAY, climate::CLIMATE_PRESET_BOOST,
climate::CLIMATE_PRESET_COMFORT, climate::CLIMATE_PRESET_HOME, climate::CLIMATE_PRESET_SLEEP,
climate::CLIMATE_PRESET_ACTIVITY}) {
if (traits.supports_preset(preset))
msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
}
for (auto const &custom_preset : traits.get_supported_custom_presets()) {
msg.supported_custom_presets.push_back(custom_preset);
}
for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL,
climate::CLIMATE_SWING_HORIZONTAL}) {
if (traits.supports_swing_mode(swing_mode))
@ -533,6 +552,12 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
call.set_away(msg.away);
if (msg.has_fan_mode)
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
if (msg.has_custom_fan_mode)
call.set_fan_mode(msg.custom_fan_mode);
if (msg.has_preset)
call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
if (msg.has_custom_preset)
call.set_preset(msg.custom_preset);
if (msg.has_swing_mode)
call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
call.perform();

View file

@ -118,8 +118,8 @@ template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMo
switch (value) {
case enums::CLIMATE_MODE_OFF:
return "CLIMATE_MODE_OFF";
case enums::CLIMATE_MODE_AUTO:
return "CLIMATE_MODE_AUTO";
case enums::CLIMATE_MODE_HEAT_COOL:
return "CLIMATE_MODE_HEAT_COOL";
case enums::CLIMATE_MODE_COOL:
return "CLIMATE_MODE_COOL";
case enums::CLIMATE_MODE_HEAT:
@ -128,6 +128,8 @@ template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMo
return "CLIMATE_MODE_FAN_ONLY";
case enums::CLIMATE_MODE_DRY:
return "CLIMATE_MODE_DRY";
case enums::CLIMATE_MODE_AUTO:
return "CLIMATE_MODE_AUTO";
default:
return "UNKNOWN";
}
@ -188,6 +190,26 @@ template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::Climate
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::ClimatePreset value) {
switch (value) {
case enums::CLIMATE_PRESET_ECO:
return "CLIMATE_PRESET_ECO";
case enums::CLIMATE_PRESET_AWAY:
return "CLIMATE_PRESET_AWAY";
case enums::CLIMATE_PRESET_BOOST:
return "CLIMATE_PRESET_BOOST";
case enums::CLIMATE_PRESET_COMFORT:
return "CLIMATE_PRESET_COMFORT";
case enums::CLIMATE_PRESET_HOME:
return "CLIMATE_PRESET_HOME";
case enums::CLIMATE_PRESET_SLEEP:
return "CLIMATE_PRESET_SLEEP";
case enums::CLIMATE_PRESET_ACTIVITY:
return "CLIMATE_PRESET_ACTIVITY";
default:
return "UNKNOWN";
}
}
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@ -2647,6 +2669,10 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
this->supported_swing_modes.push_back(value.as_enum<enums::ClimateSwingMode>());
return true;
}
case 16: {
this->supported_presets.push_back(value.as_enum<enums::ClimatePreset>());
return true;
}
default:
return false;
}
@ -2665,6 +2691,14 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
this->unique_id = value.as_string();
return true;
}
case 15: {
this->supported_custom_fan_modes.push_back(value.as_string());
return true;
}
case 17: {
this->supported_custom_presets.push_back(value.as_string());
return true;
}
default:
return false;
}
@ -2712,6 +2746,15 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->supported_swing_modes) {
buffer.encode_enum<enums::ClimateSwingMode>(14, it, true);
}
for (auto &it : this->supported_custom_fan_modes) {
buffer.encode_string(15, it, true);
}
for (auto &it : this->supported_presets) {
buffer.encode_enum<enums::ClimatePreset>(16, it, true);
}
for (auto &it : this->supported_custom_presets) {
buffer.encode_string(17, it, true);
}
}
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
char buffer[64];
@ -2781,6 +2824,24 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append(proto_enum_to_string<enums::ClimateSwingMode>(it));
out.append("\n");
}
for (const auto &it : this->supported_custom_fan_modes) {
out.append(" supported_custom_fan_modes: ");
out.append("'").append(it).append("'");
out.append("\n");
}
for (const auto &it : this->supported_presets) {
out.append(" supported_presets: ");
out.append(proto_enum_to_string<enums::ClimatePreset>(it));
out.append("\n");
}
for (const auto &it : this->supported_custom_presets) {
out.append(" supported_custom_presets: ");
out.append("'").append(it).append("'");
out.append("\n");
}
out.append("}");
}
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
@ -2805,6 +2866,24 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
return true;
}
case 12: {
this->preset = value.as_enum<enums::ClimatePreset>();
return true;
}
default:
return false;
}
}
bool ClimateStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 11: {
this->custom_fan_mode = value.as_string();
return true;
}
case 13: {
this->custom_preset = value.as_string();
return true;
}
default:
return false;
}
@ -2846,6 +2925,9 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::ClimateAction>(8, this->action);
buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
buffer.encode_string(11, this->custom_fan_mode);
buffer.encode_enum<enums::ClimatePreset>(12, this->preset);
buffer.encode_string(13, this->custom_preset);
}
void ClimateStateResponse::dump_to(std::string &out) const {
char buffer[64];
@ -2894,6 +2976,18 @@ void ClimateStateResponse::dump_to(std::string &out) const {
out.append(" swing_mode: ");
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
out.append("\n");
out.append(" custom_fan_mode: ");
out.append("'").append(this->custom_fan_mode).append("'");
out.append("\n");
out.append(" preset: ");
out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
out.append("\n");
out.append(" custom_preset: ");
out.append("'").append(this->custom_preset).append("'");
out.append("\n");
out.append("}");
}
bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
@ -2942,6 +3036,36 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
return true;
}
case 16: {
this->has_custom_fan_mode = value.as_bool();
return true;
}
case 18: {
this->has_preset = value.as_bool();
return true;
}
case 19: {
this->preset = value.as_enum<enums::ClimatePreset>();
return true;
}
case 20: {
this->has_custom_preset = value.as_bool();
return true;
}
default:
return false;
}
}
bool ClimateCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 17: {
this->custom_fan_mode = value.as_string();
return true;
}
case 21: {
this->custom_preset = value.as_string();
return true;
}
default:
return false;
}
@ -2984,6 +3108,12 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
buffer.encode_bool(14, this->has_swing_mode);
buffer.encode_enum<enums::ClimateSwingMode>(15, this->swing_mode);
buffer.encode_bool(16, this->has_custom_fan_mode);
buffer.encode_string(17, this->custom_fan_mode);
buffer.encode_bool(18, this->has_preset);
buffer.encode_enum<enums::ClimatePreset>(19, this->preset);
buffer.encode_bool(20, this->has_custom_preset);
buffer.encode_string(21, this->custom_preset);
}
void ClimateCommandRequest::dump_to(std::string &out) const {
char buffer[64];
@ -3051,6 +3181,30 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
out.append(" swing_mode: ");
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
out.append("\n");
out.append(" has_custom_fan_mode: ");
out.append(YESNO(this->has_custom_fan_mode));
out.append("\n");
out.append(" custom_fan_mode: ");
out.append("'").append(this->custom_fan_mode).append("'");
out.append("\n");
out.append(" has_preset: ");
out.append(YESNO(this->has_preset));
out.append("\n");
out.append(" preset: ");
out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
out.append("\n");
out.append(" has_custom_preset: ");
out.append(YESNO(this->has_custom_preset));
out.append("\n");
out.append(" custom_preset: ");
out.append("'").append(this->custom_preset).append("'");
out.append("\n");
out.append("}");
}

View file

@ -57,11 +57,12 @@ enum ServiceArgType : uint32_t {
};
enum ClimateMode : uint32_t {
CLIMATE_MODE_OFF = 0,
CLIMATE_MODE_AUTO = 1,
CLIMATE_MODE_HEAT_COOL = 1,
CLIMATE_MODE_COOL = 2,
CLIMATE_MODE_HEAT = 3,
CLIMATE_MODE_FAN_ONLY = 4,
CLIMATE_MODE_DRY = 5,
CLIMATE_MODE_AUTO = 6,
};
enum ClimateFanMode : uint32_t {
CLIMATE_FAN_ON = 0,
@ -88,6 +89,15 @@ enum ClimateAction : uint32_t {
CLIMATE_ACTION_DRYING = 5,
CLIMATE_ACTION_FAN = 6,
};
enum ClimatePreset : uint32_t {
CLIMATE_PRESET_ECO = 0,
CLIMATE_PRESET_AWAY = 1,
CLIMATE_PRESET_BOOST = 2,
CLIMATE_PRESET_COMFORT = 3,
CLIMATE_PRESET_HOME = 4,
CLIMATE_PRESET_SLEEP = 5,
CLIMATE_PRESET_ACTIVITY = 6,
};
} // namespace enums
@ -687,20 +697,23 @@ class CameraImageRequest : public ProtoMessage {
};
class ListEntitiesClimateResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
bool supports_current_temperature{false};
bool supports_two_point_target_temperature{false};
std::vector<enums::ClimateMode> supported_modes{};
float visual_min_temperature{0.0f};
float visual_max_temperature{0.0f};
float visual_temperature_step{0.0f};
bool supports_away{false};
bool supports_action{false};
std::vector<enums::ClimateFanMode> supported_fan_modes{};
std::vector<enums::ClimateSwingMode> supported_swing_modes{};
std::string object_id{}; // NOLINT
uint32_t key{0}; // NOLINT
std::string name{}; // NOLINT
std::string unique_id{}; // NOLINT
bool supports_current_temperature{false}; // NOLINT
bool supports_two_point_target_temperature{false}; // NOLINT
std::vector<enums::ClimateMode> supported_modes{}; // NOLINT
float visual_min_temperature{0.0f}; // NOLINT
float visual_max_temperature{0.0f}; // NOLINT
float visual_temperature_step{0.0f}; // NOLINT
bool supports_away{false}; // NOLINT
bool supports_action{false}; // NOLINT
std::vector<enums::ClimateFanMode> supported_fan_modes{}; // NOLINT
std::vector<enums::ClimateSwingMode> supported_swing_modes{}; // NOLINT
std::vector<std::string> supported_custom_fan_modes{}; // NOLINT
std::vector<enums::ClimatePreset> supported_presets{}; // NOLINT
std::vector<std::string> supported_custom_presets{}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
@ -711,45 +724,56 @@ class ListEntitiesClimateResponse : public ProtoMessage {
};
class ClimateStateResponse : public ProtoMessage {
public:
uint32_t key{0};
enums::ClimateMode mode{};
float current_temperature{0.0f};
float target_temperature{0.0f};
float target_temperature_low{0.0f};
float target_temperature_high{0.0f};
bool away{false};
enums::ClimateAction action{};
enums::ClimateFanMode fan_mode{};
enums::ClimateSwingMode swing_mode{};
uint32_t key{0}; // NOLINT
enums::ClimateMode mode{}; // NOLINT
float current_temperature{0.0f}; // NOLINT
float target_temperature{0.0f}; // NOLINT
float target_temperature_low{0.0f}; // NOLINT
float target_temperature_high{0.0f}; // NOLINT
bool away{false}; // NOLINT
enums::ClimateAction action{}; // NOLINT
enums::ClimateFanMode fan_mode{}; // NOLINT
enums::ClimateSwingMode swing_mode{}; // NOLINT
std::string custom_fan_mode{}; // NOLINT
enums::ClimatePreset preset{}; // NOLINT
std::string custom_preset{}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ClimateCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
bool has_mode{false};
enums::ClimateMode mode{};
bool has_target_temperature{false};
float target_temperature{0.0f};
bool has_target_temperature_low{false};
float target_temperature_low{0.0f};
bool has_target_temperature_high{false};
float target_temperature_high{0.0f};
bool has_away{false};
bool away{false};
bool has_fan_mode{false};
enums::ClimateFanMode fan_mode{};
bool has_swing_mode{false};
enums::ClimateSwingMode swing_mode{};
uint32_t key{0}; // NOLINT
bool has_mode{false}; // NOLINT
enums::ClimateMode mode{}; // NOLINT
bool has_target_temperature{false}; // NOLINT
float target_temperature{0.0f}; // NOLINT
bool has_target_temperature_low{false}; // NOLINT
float target_temperature_low{0.0f}; // NOLINT
bool has_target_temperature_high{false}; // NOLINT
float target_temperature_high{0.0f}; // NOLINT
bool has_away{false}; // NOLINT
bool away{false}; // NOLINT
bool has_fan_mode{false}; // NOLINT
enums::ClimateFanMode fan_mode{}; // NOLINT
bool has_swing_mode{false}; // NOLINT
enums::ClimateSwingMode swing_mode{}; // NOLINT
bool has_custom_fan_mode{false}; // NOLINT
std::string custom_fan_mode{}; // NOLINT
bool has_preset{false}; // NOLINT
enums::ClimatePreset preset{}; // NOLINT
bool has_custom_preset{false}; // NOLINT
std::string custom_preset{}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};

View file

@ -4,11 +4,14 @@ from esphome import automation
from esphome.components import mqtt
from esphome.const import (
CONF_AWAY,
CONF_CUSTOM_FAN_MODE,
CONF_CUSTOM_PRESET,
CONF_ID,
CONF_INTERNAL,
CONF_MAX_TEMPERATURE,
CONF_MIN_TEMPERATURE,
CONF_MODE,
CONF_PRESET,
CONF_TARGET_TEMPERATURE,
CONF_TARGET_TEMPERATURE_HIGH,
CONF_TARGET_TEMPERATURE_LOW,
@ -33,11 +36,12 @@ ClimateTraits = climate_ns.class_("ClimateTraits")
ClimateMode = climate_ns.enum("ClimateMode")
CLIMATE_MODES = {
"OFF": ClimateMode.CLIMATE_MODE_OFF,
"AUTO": ClimateMode.CLIMATE_MODE_AUTO,
"HEAT_COOL": ClimateMode.CLIMATE_HEAT_COOL,
"COOL": ClimateMode.CLIMATE_MODE_COOL,
"HEAT": ClimateMode.CLIMATE_MODE_HEAT,
"DRY": ClimateMode.CLIMATE_MODE_DRY,
"FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
"AUTO": ClimateMode.CLIMATE_MODE_AUTO,
}
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
@ -56,6 +60,19 @@ CLIMATE_FAN_MODES = {
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
ClimatePreset = climate_ns.enum("ClimatePreset")
CLIMATE_PRESETS = {
"ECO": ClimatePreset.CLIMATE_PRESET_ECO,
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
"COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT,
"HOME": ClimatePreset.CLIMATE_PRESET_HOME,
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
"ACTIVITY": ClimatePreset.CLIMATE_PRESET_ACTIVITY,
}
validate_climate_preset = cv.enum(CLIMATE_PRESETS, upper=True)
ClimateSwingMode = climate_ns.enum("ClimateSwingMode")
CLIMATE_SWING_MODES = {
"OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
@ -117,7 +134,12 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode),
cv.Exclusive(CONF_FAN_MODE, "fan_mode"): cv.templatable(
validate_climate_fan_mode
),
cv.Exclusive(CONF_CUSTOM_FAN_MODE, "fan_mode"): cv.string_strict,
cv.Exclusive(CONF_PRESET, "preset"): cv.templatable(validate_climate_preset),
cv.Exclusive(CONF_CUSTOM_PRESET, "preset"): cv.string_strict,
cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode),
}
)
@ -151,6 +173,15 @@ async def climate_control_to_code(config, action_id, template_arg, args):
if CONF_FAN_MODE in config:
template_ = await cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
cg.add(var.set_fan_mode(template_))
if CONF_CUSTOM_FAN_MODE in config:
template_ = await cg.templatable(config[CONF_CUSTOM_FAN_MODE], args, str)
cg.add(var.set_custom_fan_mode(template_))
if CONF_PRESET in config:
template_ = await cg.templatable(config[CONF_PRESET], args, ClimatePreset)
cg.add(var.set_preset(template_))
if CONF_CUSTOM_PRESET in config:
template_ = await cg.templatable(config[CONF_CUSTOM_PRESET], args, str)
cg.add(var.set_custom_preset(template_))
if CONF_SWING_MODE in config:
template_ = await cg.templatable(
config[CONF_SWING_MODE], args, ClimateSwingMode

View file

@ -16,6 +16,9 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
TEMPLATABLE_VALUE(float, target_temperature_high)
TEMPLATABLE_VALUE(bool, away)
TEMPLATABLE_VALUE(ClimateFanMode, fan_mode)
TEMPLATABLE_VALUE(std::string, custom_fan_mode)
TEMPLATABLE_VALUE(ClimatePreset, preset)
TEMPLATABLE_VALUE(std::string, custom_preset)
TEMPLATABLE_VALUE(ClimateSwingMode, swing_mode)
void play(Ts... x) override {
@ -26,6 +29,9 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
call.set_target_temperature_high(this->target_temperature_high_.optional_value(x...));
call.set_away(this->away_.optional_value(x...));
call.set_fan_mode(this->fan_mode_.optional_value(x...));
call.set_fan_mode(this->custom_fan_mode_.optional_value(x...));
call.set_preset(this->preset_.optional_value(x...));
call.set_preset(this->custom_preset_.optional_value(x...));
call.set_swing_mode(this->swing_mode_.optional_value(x...));
call.perform();
}

View file

@ -1,5 +1,4 @@
#include "climate.h"
#include "esphome/core/log.h"
namespace esphome {
namespace climate {
@ -13,10 +12,24 @@ void ClimateCall::perform() {
const char *mode_s = climate_mode_to_string(*this->mode_);
ESP_LOGD(TAG, " Mode: %s", mode_s);
}
if (this->custom_fan_mode_.has_value()) {
this->fan_mode_.reset();
ESP_LOGD(TAG, " Custom Fan: %s", this->custom_fan_mode_.value().c_str());
}
if (this->fan_mode_.has_value()) {
this->custom_fan_mode_.reset();
const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
ESP_LOGD(TAG, " Fan: %s", fan_mode_s);
}
if (this->custom_preset_.has_value()) {
this->preset_.reset();
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_.value().c_str());
}
if (this->preset_.has_value()) {
this->custom_preset_.reset();
const char *preset_s = climate_preset_to_string(*this->preset_);
ESP_LOGD(TAG, " Preset: %s", preset_s);
}
if (this->swing_mode_.has_value()) {
const char *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_);
ESP_LOGD(TAG, " Swing: %s", swing_mode_s);
@ -44,13 +57,32 @@ void ClimateCall::validate_() {
this->mode_.reset();
}
}
if (this->fan_mode_.has_value()) {
if (this->custom_fan_mode_.has_value()) {
auto custom_fan_mode = *this->custom_fan_mode_;
if (!traits.supports_custom_fan_mode(custom_fan_mode)) {
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", custom_fan_mode.c_str());
this->custom_fan_mode_.reset();
}
} else if (this->fan_mode_.has_value()) {
auto fan_mode = *this->fan_mode_;
if (!traits.supports_fan_mode(fan_mode)) {
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", climate_fan_mode_to_string(fan_mode));
this->fan_mode_.reset();
}
}
if (this->custom_preset_.has_value()) {
auto custom_preset = *this->custom_preset_;
if (!traits.supports_custom_preset(custom_preset)) {
ESP_LOGW(TAG, " Preset %s is not supported by this device!", custom_preset.c_str());
this->custom_preset_.reset();
}
} else if (this->preset_.has_value()) {
auto preset = *this->preset_;
if (!traits.supports_preset(preset)) {
ESP_LOGW(TAG, " Preset %s is not supported by this device!", climate_preset_to_string(preset));
this->preset_.reset();
}
}
if (this->swing_mode_.has_value()) {
auto swing_mode = *this->swing_mode_;
if (!traits.supports_swing_mode(swing_mode)) {
@ -117,6 +149,8 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
this->set_mode(CLIMATE_MODE_FAN_ONLY);
} else if (str_equals_case_insensitive(mode, "DRY")) {
this->set_mode(CLIMATE_MODE_DRY);
} else if (str_equals_case_insensitive(mode, "HEAT_COOL")) {
this->set_mode(CLIMATE_MODE_HEAT_COOL);
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str());
}
@ -124,6 +158,7 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
}
ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) {
this->fan_mode_ = fan_mode;
this->custom_fan_mode_.reset();
return *this;
}
ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
@ -146,11 +181,59 @@ ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
} else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) {
this->set_fan_mode(CLIMATE_FAN_DIFFUSE);
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str());
auto custom_fan_modes = this->parent_->get_traits().get_supported_custom_fan_modes();
if (std::find(custom_fan_modes.begin(), custom_fan_modes.end(), fan_mode) != custom_fan_modes.end()) {
this->custom_fan_mode_ = fan_mode;
this->fan_mode_.reset();
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str());
}
}
return *this;
}
ClimateCall &ClimateCall::set_fan_mode(optional<std::string> fan_mode) {
if (fan_mode.has_value()) {
this->set_fan_mode(fan_mode.value());
}
return *this;
}
ClimateCall &ClimateCall::set_preset(ClimatePreset preset) {
this->preset_ = preset;
this->custom_preset_.reset();
return *this;
}
ClimateCall &ClimateCall::set_preset(const std::string &preset) {
if (str_equals_case_insensitive(preset, "ECO")) {
this->set_preset(CLIMATE_PRESET_ECO);
} else if (str_equals_case_insensitive(preset, "AWAY")) {
this->set_preset(CLIMATE_PRESET_AWAY);
} else if (str_equals_case_insensitive(preset, "BOOST")) {
this->set_preset(CLIMATE_PRESET_BOOST);
} else if (str_equals_case_insensitive(preset, "COMFORT")) {
this->set_preset(CLIMATE_PRESET_COMFORT);
} else if (str_equals_case_insensitive(preset, "HOME")) {
this->set_preset(CLIMATE_PRESET_HOME);
} else if (str_equals_case_insensitive(preset, "SLEEP")) {
this->set_preset(CLIMATE_PRESET_SLEEP);
} else if (str_equals_case_insensitive(preset, "ACTIVITY")) {
this->set_preset(CLIMATE_PRESET_ACTIVITY);
} else {
auto custom_presets = this->parent_->get_traits().get_supported_custom_presets();
if (std::find(custom_presets.begin(), custom_presets.end(), preset) != custom_presets.end()) {
this->custom_preset_ = preset;
this->preset_.reset();
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), preset.c_str());
}
}
return *this;
}
ClimateCall &ClimateCall::set_preset(optional<std::string> preset) {
if (preset.has_value()) {
this->set_preset(preset.value());
}
return *this;
}
ClimateCall &ClimateCall::set_swing_mode(ClimateSwingMode swing_mode) {
this->swing_mode_ = swing_mode;
return *this;
@ -188,6 +271,9 @@ const optional<float> &ClimateCall::get_target_temperature_low() const { return
const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; }
const optional<bool> &ClimateCall::get_away() const { return this->away_; }
const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; }
const optional<std::string> &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; }
const optional<ClimatePreset> &ClimateCall::get_preset() const { return this->preset_; }
const optional<std::string> &ClimateCall::get_custom_preset() const { return this->custom_preset_; }
const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; }
ClimateCall &ClimateCall::set_away(bool away) {
this->away_ = away;
@ -215,6 +301,12 @@ ClimateCall &ClimateCall::set_mode(optional<ClimateMode> mode) {
}
ClimateCall &ClimateCall::set_fan_mode(optional<ClimateFanMode> fan_mode) {
this->fan_mode_ = fan_mode;
this->custom_fan_mode_.reset();
return *this;
}
ClimateCall &ClimateCall::set_preset(optional<ClimatePreset> preset) {
this->preset_ = preset;
this->custom_preset_.reset();
return *this;
}
ClimateCall &ClimateCall::set_swing_mode(optional<ClimateSwingMode> swing_mode) {
@ -249,8 +341,31 @@ void Climate::save_state_() {
if (traits.get_supports_away()) {
state.away = this->away;
}
if (traits.get_supports_fan_modes()) {
state.fan_mode = this->fan_mode;
if (traits.get_supports_fan_modes() && fan_mode.has_value()) {
state.uses_custom_fan_mode = false;
state.fan_mode = this->fan_mode.value();
}
if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) {
state.uses_custom_fan_mode = true;
auto &custom_fan_modes = traits.get_supported_custom_fan_modes();
auto it = std::find(custom_fan_modes.begin(), custom_fan_modes.end(), this->custom_fan_mode.value());
// only set custom fan mode if value exists, otherwise leave it as is
if (it != custom_fan_modes.cend()) {
state.custom_fan_mode = std::distance(custom_fan_modes.begin(), it);
}
}
if (traits.get_supports_presets() && preset.has_value()) {
state.uses_custom_preset = false;
state.preset = this->preset.value();
}
if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) {
state.uses_custom_preset = true;
auto custom_presets = traits.get_supported_custom_presets();
auto it = std::find(custom_presets.begin(), custom_presets.end(), this->custom_preset.value());
// only set custom preset if value exists, otherwise leave it as is
if (it != custom_presets.cend()) {
state.custom_preset = std::distance(custom_presets.begin(), it);
}
}
if (traits.get_supports_swing_modes()) {
state.swing_mode = this->swing_mode;
@ -266,8 +381,17 @@ void Climate::publish_state() {
if (traits.get_supports_action()) {
ESP_LOGD(TAG, " Action: %s", climate_action_to_string(this->action));
}
if (traits.get_supports_fan_modes()) {
ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode));
if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) {
ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode.value()));
}
if (!traits.get_supported_custom_fan_modes().empty() && this->custom_fan_mode.has_value()) {
ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode.value().c_str());
}
if (traits.get_supports_presets() && this->preset.has_value()) {
ESP_LOGD(TAG, " Preset: %s", climate_preset_to_string(this->preset.value()));
}
if (!traits.get_supported_custom_presets().empty() && this->custom_preset.has_value()) {
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset.value().c_str());
}
if (traits.get_supports_swing_modes()) {
ESP_LOGD(TAG, " Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode));
@ -332,9 +456,12 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
if (traits.get_supports_away()) {
call.set_away(this->away);
}
if (traits.get_supports_fan_modes()) {
if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) {
call.set_fan_mode(this->fan_mode);
}
if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
call.set_preset(this->preset);
}
if (traits.get_supports_swing_modes()) {
call.set_swing_mode(this->swing_mode);
}
@ -352,9 +479,21 @@ void ClimateDeviceRestoreState::apply(Climate *climate) {
if (traits.get_supports_away()) {
climate->away = this->away;
}
if (traits.get_supports_fan_modes()) {
if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) {
climate->fan_mode = this->fan_mode;
}
if (!traits.get_supported_custom_fan_modes().empty() && this->uses_custom_fan_mode) {
climate->custom_fan_mode = traits.get_supported_custom_fan_modes()[this->custom_fan_mode];
}
if (traits.get_supports_presets() && !this->uses_custom_preset) {
climate->preset = this->preset;
}
if (!traits.get_supported_custom_presets().empty() && this->uses_custom_preset) {
climate->custom_preset = traits.get_supported_custom_presets()[this->custom_preset];
}
if (!traits.get_supported_custom_presets().empty() && uses_custom_preset) {
climate->custom_preset = traits.get_supported_custom_presets()[this->preset];
}
if (traits.get_supports_swing_modes()) {
climate->swing_mode = this->swing_mode;
}

View file

@ -3,6 +3,7 @@
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/core/log.h"
#include "climate_mode.h"
#include "climate_traits.h"
@ -70,12 +71,22 @@ class ClimateCall {
ClimateCall &set_fan_mode(optional<ClimateFanMode> fan_mode);
/// Set the fan mode of the climate device based on a string.
ClimateCall &set_fan_mode(const std::string &fan_mode);
/// Set the fan mode of the climate device based on a string.
ClimateCall &set_fan_mode(optional<std::string> fan_mode);
/// Set the swing mode of the climate device.
ClimateCall &set_swing_mode(ClimateSwingMode swing_mode);
/// Set the swing mode of the climate device.
ClimateCall &set_swing_mode(optional<ClimateSwingMode> swing_mode);
/// Set the swing mode of the climate device based on a string.
ClimateCall &set_swing_mode(const std::string &swing_mode);
/// Set the preset of the climate device.
ClimateCall &set_preset(ClimatePreset preset);
/// Set the preset of the climate device.
ClimateCall &set_preset(optional<ClimatePreset> preset);
/// Set the preset of the climate device based on a string.
ClimateCall &set_preset(const std::string &preset);
/// Set the preset of the climate device based on a string.
ClimateCall &set_preset(optional<std::string> preset);
void perform();
@ -86,6 +97,9 @@ class ClimateCall {
const optional<bool> &get_away() const;
const optional<ClimateFanMode> &get_fan_mode() const;
const optional<ClimateSwingMode> &get_swing_mode() const;
const optional<std::string> &get_custom_fan_mode() const;
const optional<ClimatePreset> &get_preset() const;
const optional<std::string> &get_custom_preset() const;
protected:
void validate_();
@ -98,13 +112,25 @@ class ClimateCall {
optional<bool> away_;
optional<ClimateFanMode> fan_mode_;
optional<ClimateSwingMode> swing_mode_;
optional<std::string> custom_fan_mode_;
optional<ClimatePreset> preset_;
optional<std::string> custom_preset_;
};
/// Struct used to save the state of the climate device in restore memory.
struct ClimateDeviceRestoreState {
ClimateMode mode;
bool away;
ClimateFanMode fan_mode;
bool uses_custom_fan_mode{false};
union {
ClimateFanMode fan_mode;
uint8_t custom_fan_mode;
};
bool uses_custom_preset{false};
union {
ClimatePreset preset;
uint8_t custom_preset;
};
ClimateSwingMode swing_mode;
union {
float target_temperature;
@ -168,11 +194,20 @@ class Climate : public Nameable {
bool away{false};
/// The active fan mode of the climate device.
ClimateFanMode fan_mode;
optional<ClimateFanMode> fan_mode;
/// The active swing mode of the climate device.
ClimateSwingMode swing_mode;
/// The active custom fan mode of the climate device.
optional<std::string> custom_fan_mode;
/// The active preset of the climate device.
optional<ClimatePreset> preset;
/// The active custom preset mode of the climate device.
optional<std::string> custom_preset;
/** Add a callback for the climate device state, each time the state of the climate device is updated
* (using publish_state), this callback will be called.
*

View file

@ -17,6 +17,8 @@ const char *climate_mode_to_string(ClimateMode mode) {
return "FAN_ONLY";
case CLIMATE_MODE_DRY:
return "DRY";
case CLIMATE_MODE_HEAT_COOL:
return "HEAT_COOL";
default:
return "UNKNOWN";
}
@ -80,5 +82,26 @@ const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
}
}
const char *climate_preset_to_string(ClimatePreset preset) {
switch (preset) {
case climate::CLIMATE_PRESET_ECO:
return "ECO";
case climate::CLIMATE_PRESET_AWAY:
return "AWAY";
case climate::CLIMATE_PRESET_BOOST:
return "BOOST";
case climate::CLIMATE_PRESET_COMFORT:
return "COMFORT";
case climate::CLIMATE_PRESET_HOME:
return "HOME";
case climate::CLIMATE_PRESET_SLEEP:
return "SLEEP";
case climate::CLIMATE_PRESET_ACTIVITY:
return "ACTIVITY";
default:
return "UNKNOWN";
}
}
} // namespace climate
} // namespace esphome

View file

@ -10,7 +10,7 @@ enum ClimateMode : uint8_t {
/// The climate device is off (not in auto, heat or cool mode)
CLIMATE_MODE_OFF = 0,
/// The climate device is set to automatically change the heating/cooling cycle
CLIMATE_MODE_AUTO = 1,
CLIMATE_MODE_HEAT_COOL = 1,
/// The climate device is manually set to cool mode (not in auto mode!)
CLIMATE_MODE_COOL = 2,
/// The climate device is manually set to heat mode (not in auto mode!)
@ -19,6 +19,8 @@ enum ClimateMode : uint8_t {
CLIMATE_MODE_FAN_ONLY = 4,
/// The climate device is manually set to dry mode
CLIMATE_MODE_DRY = 5,
/// The climate device is manually set to heat-cool mode
CLIMATE_MODE_AUTO = 6
};
/// Enum for the current action of the climate device. Values match those of ClimateMode.
@ -61,7 +63,7 @@ enum ClimateFanMode : uint8_t {
/// Enum for all modes a climate swing can be in
enum ClimateSwingMode : uint8_t {
/// The sing mode is set to Off
/// The swing mode is set to Off
CLIMATE_SWING_OFF = 0,
/// The fan mode is set to Both
CLIMATE_SWING_BOTH = 1,
@ -71,6 +73,24 @@ enum ClimateSwingMode : uint8_t {
CLIMATE_SWING_HORIZONTAL = 3,
};
/// Enum for all modes a climate swing can be in
enum ClimatePreset : uint8_t {
/// Preset is set to ECO
CLIMATE_PRESET_ECO = 0,
/// Preset is set to AWAY
CLIMATE_PRESET_AWAY = 1,
/// Preset is set to BOOST
CLIMATE_PRESET_BOOST = 2,
/// Preset is set to COMFORT
CLIMATE_PRESET_COMFORT = 3,
/// Preset is set to HOME
CLIMATE_PRESET_HOME = 4,
/// Preset is set to SLEEP
CLIMATE_PRESET_SLEEP = 5,
/// Preset is set to ACTIVITY
CLIMATE_PRESET_ACTIVITY = 6,
};
/// Convert the given ClimateMode to a human-readable string.
const char *climate_mode_to_string(ClimateMode mode);
@ -83,5 +103,8 @@ const char *climate_fan_mode_to_string(ClimateFanMode mode);
/// Convert the given ClimateSwingMode to a human-readable string.
const char *climate_swing_mode_to_string(ClimateSwingMode mode);
/// Convert the given ClimateSwingMode to a human-readable string.
const char *climate_preset_to_string(ClimatePreset preset);
} // namespace climate
} // namespace esphome

View file

@ -119,6 +119,71 @@ bool ClimateTraits::get_supports_fan_modes() const {
this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_;
}
void ClimateTraits::set_supported_custom_fan_modes(std::vector<std::string> &supported_custom_fan_modes) {
this->supported_custom_fan_modes_ = supported_custom_fan_modes;
}
const std::vector<std::string> ClimateTraits::get_supported_custom_fan_modes() const {
return this->supported_custom_fan_modes_;
}
bool ClimateTraits::supports_custom_fan_mode(std::string &custom_fan_mode) const {
return std::count(this->supported_custom_fan_modes_.begin(), this->supported_custom_fan_modes_.end(),
custom_fan_mode);
}
bool ClimateTraits::supports_preset(ClimatePreset preset) const {
switch (preset) {
case climate::CLIMATE_PRESET_ECO:
return this->supports_preset_eco_;
case climate::CLIMATE_PRESET_AWAY:
return this->supports_preset_away_;
case climate::CLIMATE_PRESET_BOOST:
return this->supports_preset_boost_;
case climate::CLIMATE_PRESET_COMFORT:
return this->supports_preset_comfort_;
case climate::CLIMATE_PRESET_HOME:
return this->supports_preset_home_;
case climate::CLIMATE_PRESET_SLEEP:
return this->supports_preset_sleep_;
case climate::CLIMATE_PRESET_ACTIVITY:
return this->supports_preset_activity_;
default:
return false;
}
}
void ClimateTraits::set_supports_preset_eco(bool supports_preset_eco) {
this->supports_preset_eco_ = supports_preset_eco;
}
void ClimateTraits::set_supports_preset_away(bool supports_preset_away) {
this->supports_preset_away_ = supports_preset_away;
}
void ClimateTraits::set_supports_preset_boost(bool supports_preset_boost) {
this->supports_preset_boost_ = supports_preset_boost;
}
void ClimateTraits::set_supports_preset_comfort(bool supports_preset_comfort) {
this->supports_preset_comfort_ = supports_preset_comfort;
}
void ClimateTraits::set_supports_preset_home(bool supports_preset_home) {
this->supports_preset_home_ = supports_preset_home;
}
void ClimateTraits::set_supports_preset_sleep(bool supports_preset_sleep) {
this->supports_preset_sleep_ = supports_preset_sleep;
}
void ClimateTraits::set_supports_preset_activity(bool supports_preset_activity) {
this->supports_preset_activity_ = supports_preset_activity;
}
bool ClimateTraits::get_supports_presets() const {
return this->supports_preset_eco_ || this->supports_preset_away_ || this->supports_preset_boost_ ||
this->supports_preset_comfort_ || this->supports_preset_home_ || this->supports_preset_sleep_ ||
this->supports_preset_activity_;
}
void ClimateTraits::set_supported_custom_presets(std::vector<std::string> &supported_custom_presets) {
this->supported_custom_presets_ = supported_custom_presets;
}
const std::vector<std::string> ClimateTraits::get_supported_custom_presets() const {
return this->supported_custom_presets_;
}
bool ClimateTraits::supports_custom_preset(std::string &custom_preset) const {
return std::count(this->supported_custom_presets_.begin(), this->supported_custom_presets_.end(), custom_preset);
}
void ClimateTraits::set_supports_swing_mode_off(bool supports_swing_mode_off) {
this->supports_swing_mode_off_ = supports_swing_mode_off;
}

View file

@ -1,5 +1,6 @@
#pragma once
#include "esphome/core/helpers.h"
#include "climate_mode.h"
namespace esphome {
@ -65,6 +66,21 @@ class ClimateTraits {
void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse);
bool supports_fan_mode(ClimateFanMode fan_mode) const;
bool get_supports_fan_modes() const;
void set_supported_custom_fan_modes(std::vector<std::string> &supported_custom_fan_modes);
const std::vector<std::string> get_supported_custom_fan_modes() const;
bool supports_custom_fan_mode(std::string &custom_fan_mode) const;
bool supports_preset(ClimatePreset preset) const;
void set_supports_preset_eco(bool supports_preset_eco);
void set_supports_preset_away(bool supports_preset_away);
void set_supports_preset_boost(bool supports_preset_boost);
void set_supports_preset_comfort(bool supports_preset_comfort);
void set_supports_preset_home(bool supports_preset_home);
void set_supports_preset_sleep(bool supports_preset_sleep);
void set_supports_preset_activity(bool supports_preset_activity);
bool get_supports_presets() const;
void set_supported_custom_presets(std::vector<std::string> &supported_custom_presets);
const std::vector<std::string> get_supported_custom_presets() const;
bool supports_custom_preset(std::string &custom_preset) const;
void set_supports_swing_mode_off(bool supports_swing_mode_off);
void set_supports_swing_mode_both(bool supports_swing_mode_both);
void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical);
@ -103,6 +119,15 @@ class ClimateTraits {
bool supports_swing_mode_both_{false};
bool supports_swing_mode_vertical_{false};
bool supports_swing_mode_horizontal_{false};
bool supports_preset_eco_{false};
bool supports_preset_away_{false};
bool supports_preset_boost_{false};
bool supports_preset_comfort_{false};
bool supports_preset_home_{false};
bool supports_preset_sleep_{false};
bool supports_preset_activity_{false};
std::vector<std::string> supported_custom_fan_modes_;
std::vector<std::string> supported_custom_presets_;
float visual_min_temperature_{10};
float visual_max_temperature_{30};

View file

@ -72,7 +72,7 @@ void LgIrClimate::transmit_state() {
remote_state |= FAN_AUTO;
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY ||
this->mode == climate::CLIMATE_MODE_HEAT) {
switch (this->fan_mode) {
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_HIGH:
remote_state |= FAN_MAX;
break;

View file

@ -93,7 +93,7 @@ void CoolixClimate::transmit_state() {
this->fan_mode = climate::CLIMATE_FAN_AUTO;
remote_state |= COOLIX_FAN_MODE_AUTO_DRY;
} else {
switch (this->fan_mode) {
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_HIGH:
remote_state |= COOLIX_FAN_MAX;
break;

View file

@ -94,7 +94,7 @@ uint8_t DaikinClimate::operation_mode_() {
uint16_t DaikinClimate::fan_speed_() {
uint16_t fan_speed;
switch (this->fan_mode) {
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_LOW:
fan_speed = DAIKIN_FAN_1 << 8;
break;

View file

@ -140,7 +140,7 @@ void FujitsuGeneralClimate::transmit_state() {
}
// Set fan
switch (this->fan_mode) {
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_HIGH:
SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_HIGH);
break;

View file

@ -164,11 +164,13 @@ void HitachiClimate::transmit_state() {
case climate::CLIMATE_MODE_OFF:
set_power_(false);
break;
default:
ESP_LOGW(TAG, "Unsupported mode: %s", climate_mode_to_string(this->mode));
}
set_temp_(static_cast<uint8_t>(this->target_temperature));
switch (this->fan_mode) {
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_LOW:
set_fan_(HITACHI_AC344_FAN_LOW);
break;

View file

@ -2,7 +2,12 @@ from esphome.components import climate, sensor
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import (
CONF_CUSTOM_FAN_MODES,
CONF_CUSTOM_PRESETS,
CONF_ID,
CONF_PRESET_BOOST,
CONF_PRESET_ECO,
CONF_PRESET_SLEEP,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
@ -18,7 +23,6 @@ from esphome.components.midea_dongle import CONF_MIDEA_DONGLE_ID, MideaDongle
AUTO_LOAD = ["climate", "sensor", "midea_dongle"]
CODEOWNERS = ["@dudanov"]
CONF_BEEPER = "beeper"
CONF_SWING_HORIZONTAL = "swing_horizontal"
CONF_SWING_BOTH = "swing_both"
@ -28,14 +32,36 @@ CONF_HUMIDITY_SETPOINT = "humidity_setpoint"
midea_ac_ns = cg.esphome_ns.namespace("midea_ac")
MideaAC = midea_ac_ns.class_("MideaAC", climate.Climate, cg.Component)
CLIMATE_CUSTOM_FAN_MODES = {
"SILENT": "silent",
"TURBO": "turbo",
}
validate_climate_custom_fan_mode = cv.enum(CLIMATE_CUSTOM_FAN_MODES, upper=True)
CLIMATE_CUSTOM_PRESETS = {
"FREEZE_PROTECTION": "freeze protection",
}
validate_climate_custom_preset = cv.enum(CLIMATE_CUSTOM_PRESETS, upper=True)
CONFIG_SCHEMA = cv.All(
climate.CLIMATE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(MideaAC),
cv.GenerateID(CONF_MIDEA_DONGLE_ID): cv.use_id(MideaDongle),
cv.Optional(CONF_BEEPER, default=False): cv.boolean,
cv.Optional(CONF_CUSTOM_FAN_MODES): cv.ensure_list(
validate_climate_custom_fan_mode
),
cv.Optional(CONF_CUSTOM_PRESETS): cv.ensure_list(
validate_climate_custom_preset
),
cv.Optional(CONF_SWING_HORIZONTAL, default=False): cv.boolean,
cv.Optional(CONF_SWING_BOTH, default=False): cv.boolean,
cv.Optional(CONF_PRESET_ECO, default=False): cv.boolean,
cv.Optional(CONF_PRESET_SLEEP, default=False): cv.boolean,
cv.Optional(CONF_PRESET_BOOST, default=False): cv.boolean,
cv.Optional(CONF_OUTDOOR_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS,
ICON_THERMOMETER,
@ -65,8 +91,15 @@ async def to_code(config):
paren = await cg.get_variable(config[CONF_MIDEA_DONGLE_ID])
cg.add(var.set_midea_dongle_parent(paren))
cg.add(var.set_beeper_feedback(config[CONF_BEEPER]))
if CONF_CUSTOM_FAN_MODES in config:
cg.add(var.set_custom_fan_modes(config[CONF_CUSTOM_FAN_MODES]))
if CONF_CUSTOM_PRESETS in config:
cg.add(var.set_custom_presets(config[CONF_CUSTOM_PRESETS]))
cg.add(var.set_swing_horizontal(config[CONF_SWING_HORIZONTAL]))
cg.add(var.set_swing_both(config[CONF_SWING_BOTH]))
cg.add(var.set_preset_eco(config[CONF_PRESET_ECO]))
cg.add(var.set_preset_sleep(config[CONF_PRESET_SLEEP]))
cg.add(var.set_preset_boost(config[CONF_PRESET_BOOST]))
if CONF_OUTDOOR_TEMPERATURE in config:
sens = await sensor.new_sensor(config[CONF_OUTDOOR_TEMPERATURE])
cg.add(var.set_outdoor_temperature_sensor(sens))

View file

@ -40,8 +40,24 @@ void MideaAC::on_frame(const midea_dongle::Frame &frame) {
set_property(this->mode, p.get_mode(), need_publish);
set_property(this->target_temperature, p.get_target_temp(), need_publish);
set_property(this->current_temperature, p.get_indoor_temp(), need_publish);
set_property(this->fan_mode, p.get_fan_mode(), need_publish);
if (p.is_custom_fan_mode()) {
this->fan_mode.reset();
optional<std::string> mode = p.get_custom_fan_mode();
set_property(this->custom_fan_mode, mode, need_publish);
} else {
this->custom_fan_mode.reset();
optional<climate::ClimateFanMode> mode = p.get_fan_mode();
set_property(this->fan_mode, mode, need_publish);
}
set_property(this->swing_mode, p.get_swing_mode(), need_publish);
if (p.is_custom_preset()) {
this->preset.reset();
optional<std::string> preset = p.get_custom_preset();
set_property(this->custom_preset, preset, need_publish);
} else {
this->custom_preset.reset();
set_property(this->preset, p.get_preset(), need_publish);
}
if (need_publish)
this->publish_state();
set_sensor(this->outdoor_sensor_, p.get_outdoor_temp());
@ -61,6 +77,48 @@ void MideaAC::on_update() {
}
}
bool MideaAC::allow_preset(climate::ClimatePreset preset) const {
switch (preset) {
case climate::CLIMATE_PRESET_ECO:
if (this->mode == climate::CLIMATE_MODE_COOL) {
return true;
} else {
ESP_LOGD(TAG, "ECO preset is only available in COOL mode");
}
break;
case climate::CLIMATE_PRESET_SLEEP:
if (this->mode == climate::CLIMATE_MODE_FAN_ONLY || this->mode == climate::CLIMATE_MODE_DRY) {
ESP_LOGD(TAG, "SLEEP preset is not available in FAN_ONLY or DRY mode");
} else {
return true;
}
break;
case climate::CLIMATE_PRESET_BOOST:
if (this->mode == climate::CLIMATE_MODE_HEAT || this->mode == climate::CLIMATE_MODE_COOL) {
return true;
} else {
ESP_LOGD(TAG, "BOOST preset is only available in HEAT or COOL mode");
}
break;
case climate::CLIMATE_PRESET_HOME:
return true;
default:
break;
}
return false;
}
bool MideaAC::allow_custom_preset(const std::string &custom_preset) const {
if (custom_preset == MIDEA_FREEZE_PROTECTION_PRESET) {
if (this->mode == climate::CLIMATE_MODE_HEAT) {
return true;
} else {
ESP_LOGD(TAG, "%s is only available in HEAT mode", MIDEA_FREEZE_PROTECTION_PRESET.c_str());
}
}
return false;
}
void MideaAC::control(const climate::ClimateCall &call) {
if (call.get_mode().has_value() && call.get_mode().value() != this->mode) {
this->cmd_frame_.set_mode(call.get_mode().value());
@ -70,14 +128,34 @@ void MideaAC::control(const climate::ClimateCall &call) {
this->cmd_frame_.set_target_temp(call.get_target_temperature().value());
this->ctrl_request_ = true;
}
if (call.get_fan_mode().has_value() && call.get_fan_mode().value() != this->fan_mode) {
if (call.get_fan_mode().has_value() &&
(!this->fan_mode.has_value() || this->fan_mode.value() != call.get_fan_mode().value())) {
this->custom_fan_mode.reset();
this->cmd_frame_.set_fan_mode(call.get_fan_mode().value());
this->ctrl_request_ = true;
}
if (call.get_custom_fan_mode().has_value() &&
(!this->custom_fan_mode.has_value() || this->custom_fan_mode.value() != call.get_custom_fan_mode().value())) {
this->fan_mode.reset();
this->cmd_frame_.set_custom_fan_mode(call.get_custom_fan_mode().value());
this->ctrl_request_ = true;
}
if (call.get_swing_mode().has_value() && call.get_swing_mode().value() != this->swing_mode) {
this->cmd_frame_.set_swing_mode(call.get_swing_mode().value());
this->ctrl_request_ = true;
}
if (call.get_preset().has_value() && this->allow_preset(call.get_preset().value()) &&
(!this->preset.has_value() || this->preset.value() != call.get_preset().value())) {
this->custom_preset.reset();
this->cmd_frame_.set_preset(call.get_preset().value());
this->ctrl_request_ = true;
}
if (call.get_custom_preset().has_value() && this->allow_custom_preset(call.get_custom_preset().value()) &&
(!this->custom_preset.has_value() || this->custom_preset.value() != call.get_custom_preset().value())) {
this->preset.reset();
this->cmd_frame_.set_custom_preset(call.get_custom_preset().value());
this->ctrl_request_ = true;
}
if (this->ctrl_request_) {
this->cmd_frame_.set_beeper_feedback(this->beeper_feedback_);
this->cmd_frame_.finalize();
@ -98,10 +176,16 @@ climate::ClimateTraits MideaAC::traits() {
traits.set_supports_fan_mode_low(true);
traits.set_supports_fan_mode_medium(true);
traits.set_supports_fan_mode_high(true);
traits.set_supported_custom_fan_modes(this->traits_custom_fan_modes_);
traits.set_supports_swing_mode_off(true);
traits.set_supports_swing_mode_vertical(true);
traits.set_supports_swing_mode_horizontal(this->traits_swing_horizontal_);
traits.set_supports_swing_mode_both(this->traits_swing_both_);
traits.set_supports_preset_home(true);
traits.set_supports_preset_eco(this->traits_preset_eco_);
traits.set_supports_preset_sleep(this->traits_preset_sleep_);
traits.set_supports_preset_boost(this->traits_preset_boost_);
traits.set_supported_custom_presets(this->traits_custom_presets_);
traits.set_supports_current_temperature(true);
return traits;
}

View file

@ -22,6 +22,15 @@ class MideaAC : public midea_dongle::MideaAppliance, public climate::Climate, pu
void set_beeper_feedback(bool state) { this->beeper_feedback_ = state; }
void set_swing_horizontal(bool state) { this->traits_swing_horizontal_ = state; }
void set_swing_both(bool state) { this->traits_swing_both_ = state; }
void set_preset_eco(bool state) { this->traits_preset_eco_ = state; }
void set_preset_sleep(bool state) { this->traits_preset_sleep_ = state; }
void set_preset_boost(bool state) { this->traits_preset_boost_ = state; }
bool allow_preset(climate::ClimatePreset preset) const;
void set_custom_fan_modes(std::vector<std::string> custom_fan_modes) {
this->traits_custom_fan_modes_ = custom_fan_modes;
}
void set_custom_presets(std::vector<std::string> custom_presets) { this->traits_custom_presets_ = custom_presets; }
bool allow_custom_preset(const std::string &custom_preset) const;
protected:
/// Override control to change settings of the climate device.
@ -41,6 +50,11 @@ class MideaAC : public midea_dongle::MideaAppliance, public climate::Climate, pu
bool beeper_feedback_{false};
bool traits_swing_horizontal_{false};
bool traits_swing_both_{false};
bool traits_preset_eco_{false};
bool traits_preset_sleep_{false};
bool traits_preset_boost_{false};
std::vector<std::string> traits_custom_fan_modes_{{}};
std::vector<std::string> traits_custom_presets_{{}};
};
} // namespace midea_ac

View file

@ -3,6 +3,11 @@
namespace esphome {
namespace midea_ac {
static const char *TAG = "midea_ac";
const std::string MIDEA_SILENT_FAN_MODE = "silent";
const std::string MIDEA_TURBO_FAN_MODE = "turbo";
const std::string MIDEA_FREEZE_PROTECTION_PRESET = "freeze protection";
const uint8_t QueryFrame::INIT[] = {0xAA, 0x22, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x41, 0x00,
0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x68};
@ -80,6 +85,54 @@ void PropertiesFrame::set_mode(climate::ClimateMode mode) {
this->pbuf_[12] |= m << 5;
}
optional<climate::ClimatePreset> PropertiesFrame::get_preset() const {
if (this->get_eco_mode()) {
return climate::CLIMATE_PRESET_ECO;
} else if (this->get_sleep_mode()) {
return climate::CLIMATE_PRESET_SLEEP;
} else if (this->get_turbo_mode()) {
return climate::CLIMATE_PRESET_BOOST;
} else {
return climate::CLIMATE_PRESET_HOME;
}
}
void PropertiesFrame::set_preset(climate::ClimatePreset preset) {
switch (preset) {
case climate::CLIMATE_PRESET_ECO:
this->set_eco_mode(true);
break;
case climate::CLIMATE_PRESET_SLEEP:
this->set_sleep_mode(true);
break;
case climate::CLIMATE_PRESET_BOOST:
this->set_turbo_mode(true);
break;
default:
break;
}
}
bool PropertiesFrame::is_custom_preset() const { return this->get_freeze_protection_mode(); }
const std::string &PropertiesFrame::get_custom_preset() const { return midea_ac::MIDEA_FREEZE_PROTECTION_PRESET; };
void PropertiesFrame::set_custom_preset(const std::string &preset) {
if (preset == MIDEA_FREEZE_PROTECTION_PRESET) {
this->set_freeze_protection_mode(true);
}
}
bool PropertiesFrame::is_custom_fan_mode() const {
switch (this->pbuf_[13]) {
case MIDEA_FAN_SILENT:
case MIDEA_FAN_TURBO:
return true;
default:
return false;
}
}
climate::ClimateFanMode PropertiesFrame::get_fan_mode() const {
switch (this->pbuf_[13]) {
case MIDEA_FAN_LOW:
@ -112,6 +165,25 @@ void PropertiesFrame::set_fan_mode(climate::ClimateFanMode mode) {
this->pbuf_[13] = m;
}
const std::string &PropertiesFrame::get_custom_fan_mode() const {
switch (this->pbuf_[13]) {
case MIDEA_FAN_SILENT:
return MIDEA_SILENT_FAN_MODE;
default:
return MIDEA_TURBO_FAN_MODE;
}
}
void PropertiesFrame::set_custom_fan_mode(const std::string &mode) {
uint8_t m;
if (mode == MIDEA_SILENT_FAN_MODE) {
m = MIDEA_FAN_SILENT;
} else {
m = MIDEA_FAN_TURBO;
}
this->pbuf_[13] = m;
}
climate::ClimateSwingMode PropertiesFrame::get_swing_mode() const {
switch (this->pbuf_[17] & 0x0F) {
case MIDEA_SWING_VERTICAL:

View file

@ -5,6 +5,10 @@
namespace esphome {
namespace midea_ac {
extern const std::string MIDEA_SILENT_FAN_MODE;
extern const std::string MIDEA_TURBO_FAN_MODE;
extern const std::string MIDEA_FREEZE_PROTECTION_PRESET;
/// Enum for all modes a Midea device can be in.
enum MideaMode : uint8_t {
/// The Midea device is set to automatically change the heating/cooling cycle
@ -23,12 +27,16 @@ enum MideaMode : uint8_t {
enum MideaFanMode : uint8_t {
/// The fan mode is set to Auto
MIDEA_FAN_AUTO = 102,
/// The fan mode is set to Silent
MIDEA_FAN_SILENT = 20,
/// The fan mode is set to Low
MIDEA_FAN_LOW = 40,
/// The fan mode is set to Medium
MIDEA_FAN_MEDIUM = 60,
/// The fan mode is set to High
MIDEA_FAN_HIGH = 80,
/// The fan mode is set to Turbo
MIDEA_FAN_TURBO = 100,
};
/// Enum for all modes a Midea swing can be in
@ -65,9 +73,13 @@ class PropertiesFrame : public midea_dongle::BaseFrame {
void set_mode(climate::ClimateMode mode);
/* FAN SPEED */
bool is_custom_fan_mode() const;
climate::ClimateFanMode get_fan_mode() const;
void set_fan_mode(climate::ClimateFanMode mode);
const std::string &get_custom_fan_mode() const;
void set_custom_fan_mode(const std::string &mode);
/* SWING MODE */
climate::ClimateSwingMode get_swing_mode() const;
void set_swing_mode(climate::ClimateSwingMode mode);
@ -82,16 +94,28 @@ class PropertiesFrame : public midea_dongle::BaseFrame {
float get_humidity_setpoint() const;
/* ECO MODE */
bool get_eco_mode() const { return this->pbuf_[19]; }
void set_eco_mode(bool state) { this->set_bytemask_(19, 0xFF, state); }
bool get_eco_mode() const { return this->pbuf_[19] & 0x10; }
void set_eco_mode(bool state) { this->set_bytemask_(19, 0x80, state); }
/* SLEEP MODE */
bool get_sleep_mode() const { return this->pbuf_[20] & 0x01; }
void set_sleep_mode(bool state) { this->set_bytemask_(20, 0x01, state); }
/* TURBO MODE */
bool get_turbo_mode() const { return this->pbuf_[20] & 0x02; }
void set_turbo_mode(bool state) { this->set_bytemask_(20, 0x02, state); }
bool get_turbo_mode() const { return this->pbuf_[18] & 0x20; }
void set_turbo_mode(bool state) { this->set_bytemask_(18, 0x20, state); }
/* FREEZE PROTECTION */
bool get_freeze_protection_mode() const { return this->pbuf_[31] & 0x80; }
void set_freeze_protection_mode(bool state) { this->set_bytemask_(31, 0x80, state); }
/* PRESET */
optional<climate::ClimatePreset> get_preset() const;
void set_preset(climate::ClimatePreset preset);
bool is_custom_preset() const;
const std::string &get_custom_preset() const;
void set_custom_preset(const std::string &preset);
/* POWER USAGE */
float get_power_usage() const;

View file

@ -35,6 +35,8 @@ void MQTTClimateComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryC
modes.add("fan_only");
if (traits.supports_mode(CLIMATE_MODE_DRY))
modes.add("dry");
if (traits.supports_mode(CLIMATE_MODE_HEAT_COOL))
modes.add("heat_cool");
if (traits.get_supports_two_point_target_temperature()) {
// temperature_low_command_topic
@ -231,6 +233,9 @@ bool MQTTClimateComponent::publish_state_() {
case CLIMATE_MODE_DRY:
mode_s = "dry";
break;
case CLIMATE_MODE_HEAT_COOL:
mode_s = "heat_cool";
break;
}
bool success = true;
if (!this->publish(this->get_mode_state_topic(), mode_s))
@ -287,7 +292,7 @@ bool MQTTClimateComponent::publish_state_() {
if (traits.get_supports_fan_modes()) {
const char *payload = "";
switch (this->device_->fan_mode) {
switch (this->device_->fan_mode.value()) {
case CLIMATE_FAN_ON:
payload = "on";
break;

View file

@ -88,7 +88,7 @@ void Tcl112Climate::transmit_state() {
// Set fan
uint8_t selected_fan;
switch (this->fan_mode) {
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_HIGH:
selected_fan = TCL112_FAN_HIGH;
break;

View file

@ -33,7 +33,7 @@ float ThermostatClimate::hysteresis() { return this->hysteresis_; }
void ThermostatClimate::refresh() {
this->switch_to_mode_(this->mode);
this->switch_to_action_(compute_action_());
this->switch_to_fan_mode_(this->fan_mode);
this->switch_to_fan_mode_(this->fan_mode.value());
this->switch_to_swing_mode_(this->swing_mode);
this->publish_state();
}

View file

@ -81,7 +81,7 @@ void WhirlpoolClimate::transmit_state() {
remote_state[3] |= (uint8_t)(temp - this->temperature_min_()) << 4;
// Fan speed
switch (this->fan_mode) {
switch (this->fan_mode.value()) {
case climate::CLIMATE_FAN_HIGH:
remote_state[2] |= WHIRLPOOL_FAN_HIGH;
break;

View file

@ -145,6 +145,10 @@ CONF_CSS_URL = "css_url"
CONF_CURRENT = "current"
CONF_CURRENT_OPERATION = "current_operation"
CONF_CURRENT_RESISTOR = "current_resistor"
CONF_CUSTOM_FAN_MODE = "custom_fan_mode"
CONF_CUSTOM_FAN_MODES = "custom_fan_modes"
CONF_CUSTOM_PRESET = "custom_preset"
CONF_CUSTOM_PRESETS = "custom_presets"
CONF_DALLAS_ID = "dallas_id"
CONF_DATA = "data"
CONF_DATA_PIN = "data_pin"
@ -449,6 +453,10 @@ CONF_POWER_FACTOR = "power_factor"
CONF_POWER_ON_VALUE = "power_on_value"
CONF_POWER_SAVE_MODE = "power_save_mode"
CONF_POWER_SUPPLY = "power_supply"
CONF_PRESET = "preset"
CONF_PRESET_BOOST = "preset_boost"
CONF_PRESET_ECO = "preset_eco"
CONF_PRESET_SLEEP = "preset_sleep"
CONF_PRESSURE = "pressure"
CONF_PRIORITY = "priority"
CONF_PROTOCOL = "protocol"

View file

@ -1525,23 +1525,6 @@ climate:
name: Toshiba Climate
- platform: hitachi_ac344
name: Hitachi Climate
- platform: midea_ac
visual:
min_temperature: 18 °C
max_temperature: 25 °C
temperature_step: 0.1 °C
name: 'Electrolux EACS'
beeper: true
outdoor_temperature:
name: 'Temp'
power_usage:
name: 'Power'
humidity_setpoint:
name: 'Hum'
midea_dongle:
uart_id: uart0
strength_icon: true
switch:
- platform: gpio

View file

@ -658,6 +658,17 @@ script:
- id: my_script
then:
- lambda: 'ESP_LOGD("main", "Hello World!");'
- id: climate_custom
then:
- climate.control:
id: midea_ac_unit
custom_preset: FREEZE_PROTECTION
custom_fan_mode: SILENT
- id: climate_preset
then:
- climate.control:
id: midea_ac_unit
preset: SLEEP
sm2135:
data_pin: GPIO12
@ -819,6 +830,32 @@ climate:
kp: 0.0
ki: 0.0
kd: 0.0
- platform: midea_ac
id: midea_ac_unit
visual:
min_temperature: 18 °C
max_temperature: 25 °C
temperature_step: 0.1 °C
name: "Electrolux EACS"
beeper: true
custom_fan_modes:
- SILENT
- TURBO
preset_eco: true
preset_sleep: true
preset_boost: true
custom_presets:
- FREEZE_PROTECTION
outdoor_temperature:
name: "Temp"
power_usage:
name: "Power"
humidity_setpoint:
name: "Hum"
midea_dongle:
uart_id: uart1
strength_icon: true
cover:
- platform: endstop

View file

@ -166,6 +166,7 @@ display:
it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red);
rotation: 0°
update_interval: 16ms
- platform: waveshare_epaper
cs_pin: GPIO23
dc_pin: GPIO23