From a9d883b65a94d609e4851ee23bb34760f648e808 Mon Sep 17 00:00:00 2001 From: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Date: Wed, 18 Dec 2024 01:47:43 +0100 Subject: [PATCH] [midea] Add Fahrenheit support to `midea_ac.follow_me` action (#7762) --- esphome/components/midea/ac_automations.h | 4 +- esphome/components/midea/air_conditioner.cpp | 12 +++--- esphome/components/midea/air_conditioner.h | 2 +- esphome/components/midea/climate.py | 6 ++- esphome/components/midea/ir_transmitter.h | 41 +++++++++++++++++--- 5 files changed, 51 insertions(+), 14 deletions(-) diff --git a/esphome/components/midea/ac_automations.h b/esphome/components/midea/ac_automations.h index 5084fd1eec..e6fffa2511 100644 --- a/esphome/components/midea/ac_automations.h +++ b/esphome/components/midea/ac_automations.h @@ -19,10 +19,12 @@ template class MideaActionBase : public Action { template class FollowMeAction : public MideaActionBase { TEMPLATABLE_VALUE(float, temperature) + TEMPLATABLE_VALUE(bool, use_fahrenheit) TEMPLATABLE_VALUE(bool, beeper) void play(Ts... x) override { - this->parent_->do_follow_me(this->temperature_.value(x...), this->beeper_.value(x...)); + this->parent_->do_follow_me(this->temperature_.value(x...), this->use_fahrenheit_.value(x...), + this->beeper_.value(x...)); } }; diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index a823680d03..247aea0488 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -1,6 +1,7 @@ #ifdef USE_ARDUINO #include "esphome/core/log.h" +#include "esphome/core/helpers.h" #include "air_conditioner.h" #include "ac_adapter.h" #include @@ -121,7 +122,7 @@ void AirConditioner::dump_config() { /* ACTIONS */ -void AirConditioner::do_follow_me(float temperature, bool beeper) { +void AirConditioner::do_follow_me(float temperature, bool use_fahrenheit, bool beeper) { #ifdef USE_REMOTE_TRANSMITTER // Check if temperature is finite (not NaN or infinite) if (!std::isfinite(temperature)) { @@ -131,13 +132,14 @@ void AirConditioner::do_follow_me(float temperature, bool beeper) { // Round and convert temperature to long, then clamp and convert it to uint8_t uint8_t temp_uint8 = - static_cast(std::max(0L, std::min(static_cast(UINT8_MAX), std::lroundf(temperature)))); + static_cast(esphome::clamp(std::lroundf(temperature), 0L, static_cast(UINT8_MAX))); - ESP_LOGD(Constants::TAG, "Follow me action called with temperature: %f °C, rounded to: %u °C", temperature, - temp_uint8); + char temp_symbol = use_fahrenheit ? 'F' : 'C'; + ESP_LOGD(Constants::TAG, "Follow me action called with temperature: %.5f °%c, rounded to: %u °%c", temperature, + temp_symbol, temp_uint8, temp_symbol); // Create and transmit the data - IrFollowMeData data(temp_uint8, beeper); + IrFollowMeData data(temp_uint8, use_fahrenheit, beeper); this->transmitter_.transmit(data); #else ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component"); diff --git a/esphome/components/midea/air_conditioner.h b/esphome/components/midea/air_conditioner.h index d809aa78f6..e70bd34e71 100644 --- a/esphome/components/midea/air_conditioner.h +++ b/esphome/components/midea/air_conditioner.h @@ -32,7 +32,7 @@ class AirConditioner : public ApplianceBase, /* ### ACTIONS ### */ /* ############### */ - void do_follow_me(float temperature, bool beeper = false); + void do_follow_me(float temperature, bool use_fahrenheit, bool beeper = false); void do_display_toggle(); void do_swing_step(); void do_beeper_on() { this->set_beeper_feedback(true); } diff --git a/esphome/components/midea/climate.py b/esphome/components/midea/climate.py index e5612796a3..b7fef5e1ab 100644 --- a/esphome/components/midea/climate.py +++ b/esphome/components/midea/climate.py @@ -18,6 +18,7 @@ from esphome.const import ( CONF_SUPPORTED_SWING_MODES, CONF_TIMEOUT, CONF_TEMPERATURE, + CONF_USE_FAHRENHEIT, DEVICE_CLASS_POWER, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, @@ -172,11 +173,10 @@ MIDEA_ACTION_BASE_SCHEMA = cv.Schema( ) # FollowMe action -MIDEA_FOLLOW_ME_MIN = 0 -MIDEA_FOLLOW_ME_MAX = 37 MIDEA_FOLLOW_ME_SCHEMA = cv.Schema( { cv.Required(CONF_TEMPERATURE): cv.templatable(cv.temperature), + cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.templatable(cv.boolean), cv.Optional(CONF_BEEPER, default=False): cv.templatable(cv.boolean), } ) @@ -186,6 +186,8 @@ MIDEA_FOLLOW_ME_SCHEMA = cv.Schema( async def follow_me_to_code(var, config, args): template_ = await cg.templatable(config[CONF_BEEPER], args, cg.bool_) cg.add(var.set_beeper(template_)) + template_ = await cg.templatable(config[CONF_USE_FAHRENHEIT], args, cg.bool_) + cg.add(var.set_use_fahrenheit(template_)) template_ = await cg.templatable(config[CONF_TEMPERATURE], args, cg.float_) cg.add(var.set_temperature(template_)) diff --git a/esphome/components/midea/ir_transmitter.h b/esphome/components/midea/ir_transmitter.h index a8b89f9b7b..eba8fc87f7 100644 --- a/esphome/components/midea/ir_transmitter.h +++ b/esphome/components/midea/ir_transmitter.h @@ -16,22 +16,53 @@ class IrFollowMeData : public IrData { IrFollowMeData() : IrData({MIDEA_TYPE_FOLLOW_ME, 0x82, 0x48, 0x7F, 0x1F}) {} // Copy from Base IrFollowMeData(const IrData &data) : IrData(data) {} - // Direct from temperature and beeper values + // Direct from temperature in celsius and beeper values IrFollowMeData(uint8_t temp, bool beeper = false) : IrFollowMeData() { - this->set_temp(temp); + this->set_temp(temp, false); + this->set_beeper(beeper); + } + // Direct from temperature, fahrenheit and beeper values + IrFollowMeData(uint8_t temp, bool fahrenheit, bool beeper) : IrFollowMeData() { + this->set_temp(temp, fahrenheit); this->set_beeper(beeper); } /* TEMPERATURE */ - uint8_t temp() const { return this->get_value_(4) - 1; } - void set_temp(uint8_t val) { this->set_value_(4, std::min(MAX_TEMP, val) + 1); } + uint8_t temp() const { + if (this->fahrenheit()) { + return this->get_value_(4) + 31; + } + return this->get_value_(4) - 1; + } + void set_temp(uint8_t val, bool fahrenheit = false) { + this->set_fahrenheit(fahrenheit); + if (this->fahrenheit()) { + // see https://github.com/esphome/feature-requests/issues/1627#issuecomment-1365639966 + val = esphome::clamp(val, MIN_TEMP_F, MAX_TEMP_F) - 31; + } else { + val = esphome::clamp(val, MIN_TEMP_C, MAX_TEMP_C) + 1; + } + this->set_value_(4, val); + } /* BEEPER */ bool beeper() const { return this->get_value_(3, 128); } void set_beeper(bool val) { this->set_mask_(3, val, 128); } + /* FAHRENHEIT */ + bool fahrenheit() const { return this->get_value_(2, 32); } + void set_fahrenheit(bool val) { this->set_mask_(2, val, 32); } + protected: - static const uint8_t MAX_TEMP = 37; + static const uint8_t MIN_TEMP_C = 0; + static const uint8_t MAX_TEMP_C = 37; + + // see + // https://github.com/crankyoldgit/IRremoteESP8266/blob/9bdf8abcb465268c5409db99dc83a26df64c7445/src/ir_Midea.h#L116 + static const uint8_t MIN_TEMP_F = 32; + // see + // https://github.com/crankyoldgit/IRremoteESP8266/blob/9bdf8abcb465268c5409db99dc83a26df64c7445/src/ir_Midea.h#L117 + static const uint8_t MAX_TEMP_F = 99; }; class IrSpecialData : public IrData {