1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-24 19:48:17 +00:00
esphome/esphome/components/midea/air_conditioner.cpp

176 lines
6.7 KiB
C++

#ifdef USE_ARDUINO
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "air_conditioner.h"
#include "ac_adapter.h"
#include <cmath>
#include <cstdint>
namespace esphome {
namespace midea {
namespace ac {
static void set_sensor(Sensor *sensor, float value) {
if (sensor != nullptr && (!sensor->has_state() || sensor->get_raw_state() != value))
sensor->publish_state(value);
}
template<typename T> void update_property(T &property, const T &value, bool &flag) {
if (property != value) {
property = value;
flag = true;
}
}
void AirConditioner::on_status_change() {
bool need_publish = false;
update_property(this->target_temperature, this->base_.getTargetTemp(), need_publish);
update_property(this->current_temperature, this->base_.getIndoorTemp(), need_publish);
auto mode = Converters::to_climate_mode(this->base_.getMode());
update_property(this->mode, mode, need_publish);
auto swing_mode = Converters::to_climate_swing_mode(this->base_.getSwingMode());
update_property(this->swing_mode, swing_mode, need_publish);
// Preset
auto preset = this->base_.getPreset();
if (Converters::is_custom_midea_preset(preset)) {
if (this->set_custom_preset_(Converters::to_custom_climate_preset(preset)))
need_publish = true;
} else if (this->set_preset_(Converters::to_climate_preset(preset))) {
need_publish = true;
}
// Fan mode
auto fan_mode = this->base_.getFanMode();
if (Converters::is_custom_midea_fan_mode(fan_mode)) {
if (this->set_custom_fan_mode_(Converters::to_custom_climate_fan_mode(fan_mode)))
need_publish = true;
} else if (this->set_fan_mode_(Converters::to_climate_fan_mode(fan_mode))) {
need_publish = true;
}
if (need_publish)
this->publish_state();
set_sensor(this->outdoor_sensor_, this->base_.getOutdoorTemp());
set_sensor(this->power_sensor_, this->base_.getPowerUsage());
set_sensor(this->humidity_sensor_, this->base_.getIndoorHum());
}
void AirConditioner::control(const ClimateCall &call) {
dudanov::midea::ac::Control ctrl{};
if (call.get_target_temperature().has_value())
ctrl.targetTemp = call.get_target_temperature().value();
if (call.get_swing_mode().has_value())
ctrl.swingMode = Converters::to_midea_swing_mode(call.get_swing_mode().value());
if (call.get_mode().has_value())
ctrl.mode = Converters::to_midea_mode(call.get_mode().value());
if (call.get_preset().has_value()) {
ctrl.preset = Converters::to_midea_preset(call.get_preset().value());
} else if (call.get_custom_preset().has_value()) {
ctrl.preset = Converters::to_midea_preset(call.get_custom_preset().value());
}
if (call.get_fan_mode().has_value()) {
ctrl.fanMode = Converters::to_midea_fan_mode(call.get_fan_mode().value());
} else if (call.get_custom_fan_mode().has_value()) {
ctrl.fanMode = Converters::to_midea_fan_mode(call.get_custom_fan_mode().value());
}
this->base_.control(ctrl);
}
ClimateTraits AirConditioner::traits() {
auto traits = ClimateTraits();
traits.set_supports_current_temperature(true);
traits.set_visual_min_temperature(17);
traits.set_visual_max_temperature(30);
traits.set_visual_temperature_step(0.5);
traits.set_supported_modes(this->supported_modes_);
traits.set_supported_swing_modes(this->supported_swing_modes_);
traits.set_supported_presets(this->supported_presets_);
traits.set_supported_custom_presets(this->supported_custom_presets_);
traits.set_supported_custom_fan_modes(this->supported_custom_fan_modes_);
/* + MINIMAL SET OF CAPABILITIES */
traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_AUTO);
traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_LOW);
traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_MEDIUM);
traits.add_supported_fan_mode(ClimateFanMode::CLIMATE_FAN_HIGH);
if (this->base_.getAutoconfStatus() == dudanov::midea::AUTOCONF_OK)
Converters::to_climate_traits(traits, this->base_.getCapabilities());
if (!traits.get_supported_modes().empty())
traits.add_supported_mode(ClimateMode::CLIMATE_MODE_OFF);
if (!traits.get_supported_swing_modes().empty())
traits.add_supported_swing_mode(ClimateSwingMode::CLIMATE_SWING_OFF);
if (!traits.get_supported_presets().empty())
traits.add_supported_preset(ClimatePreset::CLIMATE_PRESET_NONE);
return traits;
}
void AirConditioner::dump_config() {
ESP_LOGCONFIG(Constants::TAG, "MideaDongle:");
ESP_LOGCONFIG(Constants::TAG, " [x] Period: %dms", this->base_.getPeriod());
ESP_LOGCONFIG(Constants::TAG, " [x] Response timeout: %dms", this->base_.getTimeout());
ESP_LOGCONFIG(Constants::TAG, " [x] Request attempts: %d", this->base_.getNumAttempts());
#ifdef USE_REMOTE_TRANSMITTER
ESP_LOGCONFIG(Constants::TAG, " [x] Using RemoteTransmitter");
#endif
if (this->base_.getAutoconfStatus() == dudanov::midea::AUTOCONF_OK) {
this->base_.getCapabilities().dump();
} else if (this->base_.getAutoconfStatus() == dudanov::midea::AUTOCONF_ERROR) {
ESP_LOGW(Constants::TAG,
"Failed to get 0xB5 capabilities report. Suggest to disable it in config and manually set your "
"appliance options.");
}
this->dump_traits_(Constants::TAG);
}
/* ACTIONS */
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)) {
ESP_LOGW(Constants::TAG, "Follow me action requires a finite temperature, got: %f", temperature);
return;
}
// Round and convert temperature to long, then clamp and convert it to uint8_t
uint8_t temp_uint8 =
static_cast<uint8_t>(esphome::clamp<long>(std::lroundf(temperature), 0L, static_cast<long>(UINT8_MAX)));
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, use_fahrenheit, beeper);
this->transmitter_.transmit(data);
#else
ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component");
#endif
}
void AirConditioner::do_swing_step() {
#ifdef USE_REMOTE_TRANSMITTER
IrSpecialData data(0x01);
this->transmitter_.transmit(data);
#else
ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component");
#endif
}
void AirConditioner::do_display_toggle() {
if (this->base_.getCapabilities().supportLightControl()) {
this->base_.displayToggle();
} else {
#ifdef USE_REMOTE_TRANSMITTER
IrSpecialData data(0x08);
this->transmitter_.transmit(data);
#else
ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component");
#endif
}
}
} // namespace ac
} // namespace midea
} // namespace esphome
#endif // USE_ARDUINO