mirror of
https://github.com/esphome/esphome.git
synced 2025-10-29 22:24:26 +00:00
Midea support v2 (#2188)
This commit is contained in:
committed by
GitHub
parent
2790d72bff
commit
4e120a291e
@@ -1,115 +1,3 @@
|
||||
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,
|
||||
UNIT_WATT,
|
||||
ICON_THERMOMETER,
|
||||
ICON_POWER,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ICON_WATER_PERCENT,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
)
|
||||
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"
|
||||
CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature"
|
||||
CONF_POWER_USAGE = "power_usage"
|
||||
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_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER_USAGE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
icon=ICON_POWER,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY_SETPOINT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_WATER_PERCENT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await climate.register_climate(var, 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))
|
||||
if CONF_POWER_USAGE in config:
|
||||
sens = await sensor.new_sensor(config[CONF_POWER_USAGE])
|
||||
cg.add(var.set_power_sensor(sens))
|
||||
if CONF_HUMIDITY_SETPOINT in config:
|
||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY_SETPOINT])
|
||||
cg.add(var.set_humidity_setpoint_sensor(sens))
|
||||
CONFIG_SCHEMA = cv.invalid("This platform has been renamed to midea in 2021.9")
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "midea_climate.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace midea_ac {
|
||||
|
||||
static const char *const TAG = "midea_ac";
|
||||
|
||||
static void set_sensor(sensor::Sensor *sensor, float value) {
|
||||
if (sensor != nullptr && (!sensor->has_state() || sensor->get_raw_state() != value))
|
||||
sensor->publish_state(value);
|
||||
}
|
||||
|
||||
template<typename T> void set_property(T &property, T value, bool &flag) {
|
||||
if (property != value) {
|
||||
property = value;
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MideaAC::on_frame(const midea_dongle::Frame &frame) {
|
||||
const auto p = frame.as<PropertiesFrame>();
|
||||
if (p.has_power_info()) {
|
||||
set_sensor(this->power_sensor_, p.get_power_usage());
|
||||
return;
|
||||
} else if (!p.has_properties()) {
|
||||
ESP_LOGW(TAG, "RX: frame has unknown type");
|
||||
return;
|
||||
}
|
||||
if (p.get_type() == midea_dongle::MideaMessageType::DEVICE_CONTROL) {
|
||||
ESP_LOGD(TAG, "RX: control frame");
|
||||
this->ctrl_request_ = false;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "RX: query frame");
|
||||
}
|
||||
if (this->ctrl_request_)
|
||||
return;
|
||||
this->cmd_frame_.set_properties(p); // copy properties from response
|
||||
bool need_publish = false;
|
||||
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);
|
||||
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());
|
||||
set_sensor(this->humidity_sensor_, p.get_humidity_setpoint());
|
||||
}
|
||||
|
||||
void MideaAC::on_update() {
|
||||
if (this->ctrl_request_) {
|
||||
ESP_LOGD(TAG, "TX: control");
|
||||
this->parent_->write_frame(this->cmd_frame_);
|
||||
} else {
|
||||
ESP_LOGD(TAG, "TX: query");
|
||||
if (this->power_sensor_ == nullptr || this->request_num_++ % 32)
|
||||
this->parent_->write_frame(this->query_frame_);
|
||||
else
|
||||
this->parent_->write_frame(this->power_frame_);
|
||||
}
|
||||
}
|
||||
|
||||
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_NONE:
|
||||
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());
|
||||
this->ctrl_request_ = true;
|
||||
}
|
||||
if (call.get_target_temperature().has_value() && call.get_target_temperature().value() != this->target_temperature) {
|
||||
this->cmd_frame_.set_target_temp(call.get_target_temperature().value());
|
||||
this->ctrl_request_ = true;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
climate::ClimateTraits MideaAC::traits() {
|
||||
auto traits = climate::ClimateTraits();
|
||||
traits.set_visual_min_temperature(17);
|
||||
traits.set_visual_max_temperature(30);
|
||||
traits.set_visual_temperature_step(0.5);
|
||||
traits.set_supported_modes({
|
||||
climate::CLIMATE_MODE_OFF,
|
||||
climate::CLIMATE_MODE_HEAT_COOL,
|
||||
climate::CLIMATE_MODE_COOL,
|
||||
climate::CLIMATE_MODE_DRY,
|
||||
climate::CLIMATE_MODE_HEAT,
|
||||
climate::CLIMATE_MODE_FAN_ONLY,
|
||||
});
|
||||
traits.set_supported_fan_modes({
|
||||
climate::CLIMATE_FAN_AUTO,
|
||||
climate::CLIMATE_FAN_LOW,
|
||||
climate::CLIMATE_FAN_MEDIUM,
|
||||
climate::CLIMATE_FAN_HIGH,
|
||||
});
|
||||
traits.set_supported_custom_fan_modes(this->traits_custom_fan_modes_);
|
||||
traits.set_supported_swing_modes({
|
||||
climate::CLIMATE_SWING_OFF,
|
||||
climate::CLIMATE_SWING_VERTICAL,
|
||||
});
|
||||
if (traits_swing_horizontal_)
|
||||
traits.add_supported_swing_mode(climate::CLIMATE_SWING_HORIZONTAL);
|
||||
if (traits_swing_both_)
|
||||
traits.add_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
|
||||
traits.set_supported_presets({
|
||||
climate::CLIMATE_PRESET_NONE,
|
||||
});
|
||||
if (traits_preset_eco_)
|
||||
traits.add_supported_preset(climate::CLIMATE_PRESET_ECO);
|
||||
if (traits_preset_sleep_)
|
||||
traits.add_supported_preset(climate::CLIMATE_PRESET_SLEEP);
|
||||
if (traits_preset_boost_)
|
||||
traits.add_supported_preset(climate::CLIMATE_PRESET_BOOST);
|
||||
traits.set_supported_custom_presets(this->traits_custom_presets_);
|
||||
traits.set_supports_current_temperature(true);
|
||||
return traits;
|
||||
}
|
||||
|
||||
} // namespace midea_ac
|
||||
} // namespace esphome
|
||||
@@ -1,68 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/midea_dongle/midea_dongle.h"
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#include "esphome/components/midea_dongle/midea_dongle.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "midea_frame.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace midea_ac {
|
||||
|
||||
class MideaAC : public midea_dongle::MideaAppliance, public climate::Climate, public Component {
|
||||
public:
|
||||
float get_setup_priority() const override { return setup_priority::LATE; }
|
||||
void on_frame(const midea_dongle::Frame &frame) override;
|
||||
void on_update() override;
|
||||
void setup() override { this->parent_->set_appliance(this); }
|
||||
void set_midea_dongle_parent(midea_dongle::MideaDongle *parent) { this->parent_ = parent; }
|
||||
void set_outdoor_temperature_sensor(sensor::Sensor *sensor) { this->outdoor_sensor_ = sensor; }
|
||||
void set_humidity_setpoint_sensor(sensor::Sensor *sensor) { this->humidity_sensor_ = sensor; }
|
||||
void set_power_sensor(sensor::Sensor *sensor) { this->power_sensor_ = sensor; }
|
||||
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::set<std::string> custom_fan_modes) {
|
||||
this->traits_custom_fan_modes_ = std::move(custom_fan_modes);
|
||||
}
|
||||
void set_custom_presets(std::set<std::string> custom_presets) {
|
||||
this->traits_custom_presets_ = std::move(custom_presets);
|
||||
}
|
||||
bool allow_custom_preset(const std::string &custom_preset) const;
|
||||
|
||||
protected:
|
||||
/// Override control to change settings of the climate device.
|
||||
void control(const climate::ClimateCall &call) override;
|
||||
/// Return the traits of this controller.
|
||||
climate::ClimateTraits traits() override;
|
||||
|
||||
const QueryFrame query_frame_;
|
||||
const PowerQueryFrame power_frame_;
|
||||
CommandFrame cmd_frame_;
|
||||
midea_dongle::MideaDongle *parent_{nullptr};
|
||||
sensor::Sensor *outdoor_sensor_{nullptr};
|
||||
sensor::Sensor *humidity_sensor_{nullptr};
|
||||
sensor::Sensor *power_sensor_{nullptr};
|
||||
uint8_t request_num_{0};
|
||||
bool ctrl_request_{false};
|
||||
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::set<std::string> traits_custom_fan_modes_{{}};
|
||||
std::set<std::string> traits_custom_presets_{{}};
|
||||
};
|
||||
|
||||
} // namespace midea_ac
|
||||
} // namespace esphome
|
||||
@@ -1,238 +0,0 @@
|
||||
#include "midea_frame.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace midea_ac {
|
||||
|
||||
static const char *const 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, 0x21, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x41, 0x81,
|
||||
0x00, 0xFF, 0x03, 0xFF, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x37, 0x31};
|
||||
|
||||
const uint8_t PowerQueryFrame::INIT[] = {0xAA, 0x22, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x41, 0x21,
|
||||
0x01, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x17, 0x6A};
|
||||
|
||||
const uint8_t CommandFrame::INIT[] = {0xAA, 0x22, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x40, 0x00,
|
||||
0x00, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
float PropertiesFrame::get_target_temp() const {
|
||||
float temp = static_cast<float>((this->pbuf_[12] & 0x0F) + 16);
|
||||
if (this->pbuf_[12] & 0x10)
|
||||
temp += 0.5;
|
||||
return temp;
|
||||
}
|
||||
|
||||
void PropertiesFrame::set_target_temp(float temp) {
|
||||
uint8_t tmp = static_cast<uint8_t>(temp * 16.0) + 4;
|
||||
tmp = ((tmp & 8) << 1) | (tmp >> 4);
|
||||
this->pbuf_[12] &= ~0x1F;
|
||||
this->pbuf_[12] |= tmp;
|
||||
}
|
||||
|
||||
static float i16tof(int16_t in) { return static_cast<float>(in - 50) / 2.0; }
|
||||
float PropertiesFrame::get_indoor_temp() const { return i16tof(this->pbuf_[21]); }
|
||||
float PropertiesFrame::get_outdoor_temp() const { return i16tof(this->pbuf_[22]); }
|
||||
float PropertiesFrame::get_humidity_setpoint() const { return static_cast<float>(this->pbuf_[29] & 0x7F); }
|
||||
|
||||
climate::ClimateMode PropertiesFrame::get_mode() const {
|
||||
if (!this->get_power_())
|
||||
return climate::CLIMATE_MODE_OFF;
|
||||
switch (this->pbuf_[12] >> 5) {
|
||||
case MIDEA_MODE_AUTO:
|
||||
return climate::CLIMATE_MODE_HEAT_COOL;
|
||||
case MIDEA_MODE_COOL:
|
||||
return climate::CLIMATE_MODE_COOL;
|
||||
case MIDEA_MODE_DRY:
|
||||
return climate::CLIMATE_MODE_DRY;
|
||||
case MIDEA_MODE_HEAT:
|
||||
return climate::CLIMATE_MODE_HEAT;
|
||||
case MIDEA_MODE_FAN_ONLY:
|
||||
return climate::CLIMATE_MODE_FAN_ONLY;
|
||||
default:
|
||||
return climate::CLIMATE_MODE_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesFrame::set_mode(climate::ClimateMode mode) {
|
||||
uint8_t m;
|
||||
switch (mode) {
|
||||
case climate::CLIMATE_MODE_HEAT_COOL:
|
||||
m = MIDEA_MODE_AUTO;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_COOL:
|
||||
m = MIDEA_MODE_COOL;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
m = MIDEA_MODE_DRY;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
m = MIDEA_MODE_HEAT;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||
m = MIDEA_MODE_FAN_ONLY;
|
||||
break;
|
||||
default:
|
||||
this->set_power_(false);
|
||||
return;
|
||||
}
|
||||
this->set_power_(true);
|
||||
this->pbuf_[12] &= ~0xE0;
|
||||
this->pbuf_[12] |= m << 5;
|
||||
}
|
||||
|
||||
optional<climate::ClimatePreset> PropertiesFrame::get_preset() const {
|
||||
if (this->get_eco_mode())
|
||||
return climate::CLIMATE_PRESET_ECO;
|
||||
if (this->get_sleep_mode())
|
||||
return climate::CLIMATE_PRESET_SLEEP;
|
||||
if (this->get_turbo_mode())
|
||||
return climate::CLIMATE_PRESET_BOOST;
|
||||
return climate::CLIMATE_PRESET_NONE;
|
||||
}
|
||||
|
||||
void PropertiesFrame::set_preset(climate::ClimatePreset preset) {
|
||||
this->clear_presets();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesFrame::clear_presets() {
|
||||
this->set_eco_mode(false);
|
||||
this->set_sleep_mode(false);
|
||||
this->set_turbo_mode(false);
|
||||
this->set_freeze_protection_mode(false);
|
||||
}
|
||||
|
||||
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) {
|
||||
this->clear_presets();
|
||||
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:
|
||||
return climate::CLIMATE_FAN_LOW;
|
||||
case MIDEA_FAN_MEDIUM:
|
||||
return climate::CLIMATE_FAN_MEDIUM;
|
||||
case MIDEA_FAN_HIGH:
|
||||
return climate::CLIMATE_FAN_HIGH;
|
||||
default:
|
||||
return climate::CLIMATE_FAN_AUTO;
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesFrame::set_fan_mode(climate::ClimateFanMode mode) {
|
||||
uint8_t m;
|
||||
switch (mode) {
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
m = MIDEA_FAN_LOW;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
m = MIDEA_FAN_MEDIUM;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
m = MIDEA_FAN_HIGH;
|
||||
break;
|
||||
default:
|
||||
m = MIDEA_FAN_AUTO;
|
||||
break;
|
||||
}
|
||||
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:
|
||||
return climate::CLIMATE_SWING_VERTICAL;
|
||||
case MIDEA_SWING_HORIZONTAL:
|
||||
return climate::CLIMATE_SWING_HORIZONTAL;
|
||||
case MIDEA_SWING_BOTH:
|
||||
return climate::CLIMATE_SWING_BOTH;
|
||||
default:
|
||||
return climate::CLIMATE_SWING_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
void PropertiesFrame::set_swing_mode(climate::ClimateSwingMode mode) {
|
||||
uint8_t m;
|
||||
switch (mode) {
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
m = MIDEA_SWING_VERTICAL;
|
||||
break;
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
m = MIDEA_SWING_HORIZONTAL;
|
||||
break;
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
m = MIDEA_SWING_BOTH;
|
||||
break;
|
||||
default:
|
||||
m = MIDEA_SWING_OFF;
|
||||
break;
|
||||
}
|
||||
this->pbuf_[17] = 0x30 | m;
|
||||
}
|
||||
|
||||
float PropertiesFrame::get_power_usage() const {
|
||||
uint32_t power = 0;
|
||||
const uint8_t *ptr = this->pbuf_ + 28;
|
||||
for (uint32_t weight = 1;; weight *= 10, ptr--) {
|
||||
power += (*ptr % 16) * weight;
|
||||
weight *= 10;
|
||||
power += (*ptr / 16) * weight;
|
||||
if (weight == 100000)
|
||||
return static_cast<float>(power) * 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace midea_ac
|
||||
} // namespace esphome
|
||||
@@ -1,165 +0,0 @@
|
||||
#pragma once
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#include "esphome/components/midea_dongle/midea_frame.h"
|
||||
|
||||
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
|
||||
MIDEA_MODE_AUTO = 1,
|
||||
/// The Midea device is manually set to cool mode (not in auto mode!)
|
||||
MIDEA_MODE_COOL = 2,
|
||||
/// The Midea device is manually set to dry mode
|
||||
MIDEA_MODE_DRY = 3,
|
||||
/// The Midea device is manually set to heat mode (not in auto mode!)
|
||||
MIDEA_MODE_HEAT = 4,
|
||||
/// The Midea device is manually set to fan only mode
|
||||
MIDEA_MODE_FAN_ONLY = 5,
|
||||
};
|
||||
|
||||
/// Enum for all modes a Midea fan can be in
|
||||
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
|
||||
enum MideaSwingMode : uint8_t {
|
||||
/// The sing mode is set to Off
|
||||
MIDEA_SWING_OFF = 0b0000,
|
||||
/// The fan mode is set to Both
|
||||
MIDEA_SWING_BOTH = 0b1111,
|
||||
/// The fan mode is set to Vertical
|
||||
MIDEA_SWING_VERTICAL = 0b1100,
|
||||
/// The fan mode is set to Horizontal
|
||||
MIDEA_SWING_HORIZONTAL = 0b0011,
|
||||
};
|
||||
|
||||
class PropertiesFrame : public midea_dongle::BaseFrame {
|
||||
public:
|
||||
PropertiesFrame() = delete;
|
||||
PropertiesFrame(uint8_t *data) : BaseFrame(data) {}
|
||||
PropertiesFrame(const Frame &frame) : BaseFrame(frame) {}
|
||||
|
||||
bool has_properties() const {
|
||||
return this->has_response_type(0xC0) && (this->has_type(0x03) || this->has_type(0x02));
|
||||
}
|
||||
|
||||
bool has_power_info() const { return this->has_response_type(0xC1); }
|
||||
|
||||
/* TARGET TEMPERATURE */
|
||||
|
||||
float get_target_temp() const;
|
||||
void set_target_temp(float temp);
|
||||
|
||||
/* MODE */
|
||||
climate::ClimateMode get_mode() const;
|
||||
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);
|
||||
|
||||
/* INDOOR TEMPERATURE */
|
||||
float get_indoor_temp() const;
|
||||
|
||||
/* OUTDOOR TEMPERATURE */
|
||||
float get_outdoor_temp() const;
|
||||
|
||||
/* HUMIDITY SETPOINT */
|
||||
float get_humidity_setpoint() const;
|
||||
|
||||
/* ECO MODE */
|
||||
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_[18] & 0x20 || this->pbuf_[20] & 0x02; }
|
||||
void set_turbo_mode(bool state) {
|
||||
this->set_bytemask_(18, 0x20, state);
|
||||
this->set_bytemask_(20, 0x02, 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);
|
||||
void clear_presets();
|
||||
|
||||
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;
|
||||
|
||||
/// Set properties from another frame
|
||||
void set_properties(const PropertiesFrame &p) { memcpy(this->pbuf_ + 11, p.data() + 11, 10); }
|
||||
|
||||
protected:
|
||||
/* POWER */
|
||||
bool get_power_() const { return this->pbuf_[11] & 0x01; }
|
||||
void set_power_(bool state) { this->set_bytemask_(11, 0x01, state); }
|
||||
};
|
||||
|
||||
// Query state frame (read-only)
|
||||
class QueryFrame : public midea_dongle::StaticFrame<midea_dongle::Frame> {
|
||||
public:
|
||||
QueryFrame() : StaticFrame(FPSTR(this->INIT)) {}
|
||||
|
||||
private:
|
||||
static const uint8_t PROGMEM INIT[];
|
||||
};
|
||||
|
||||
// Power query state frame (read-only)
|
||||
class PowerQueryFrame : public midea_dongle::StaticFrame<midea_dongle::Frame> {
|
||||
public:
|
||||
PowerQueryFrame() : StaticFrame(FPSTR(this->INIT)) {}
|
||||
|
||||
private:
|
||||
static const uint8_t PROGMEM INIT[];
|
||||
};
|
||||
|
||||
// Command frame
|
||||
class CommandFrame : public midea_dongle::StaticFrame<PropertiesFrame> {
|
||||
public:
|
||||
CommandFrame() : StaticFrame(FPSTR(this->INIT)) {}
|
||||
void set_beeper_feedback(bool state) { this->set_bytemask_(11, 0x40, state); }
|
||||
|
||||
private:
|
||||
static const uint8_t PROGMEM INIT[];
|
||||
};
|
||||
|
||||
} // namespace midea_ac
|
||||
} // namespace esphome
|
||||
Reference in New Issue
Block a user