forked from esphome/esphome
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: Add GREE climateir component (esphome#4464)
Co-authored-by: orestismers <[email protected]> Co-authored-by: Orestes Mersinias <[email protected]> Co-authored-by: Jesse Hills <[email protected]>
- Loading branch information
1 parent
b4765fb
commit 96822aa
Showing
6 changed files
with
291 additions
and
0 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import esphome.codegen as cg | ||
import esphome.config_validation as cv | ||
from esphome.components import climate_ir | ||
from esphome.const import CONF_ID, CONF_MODEL | ||
|
||
CODEOWNERS = ["@orestismers"] | ||
|
||
AUTO_LOAD = ["climate_ir"] | ||
|
||
gree_ns = cg.esphome_ns.namespace("gree") | ||
GreeClimate = gree_ns.class_("GreeClimate", climate_ir.ClimateIR) | ||
|
||
Model = gree_ns.enum("Model") | ||
MODELS = { | ||
"generic": Model.GREE_GENERIC, | ||
"yan": Model.GREE_YAN, | ||
"yaa": Model.GREE_YAA, | ||
"yac": Model.GREE_YAC, | ||
} | ||
|
||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( | ||
{ | ||
cv.GenerateID(): cv.declare_id(GreeClimate), | ||
cv.Required(CONF_MODEL): cv.enum(MODELS), | ||
} | ||
) | ||
|
||
|
||
async def to_code(config): | ||
var = cg.new_Pvariable(config[CONF_ID]) | ||
cg.add(var.set_model(config[CONF_MODEL])) | ||
|
||
await climate_ir.register_climate_ir(var, config) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
#include "gree.h" | ||
#include "esphome/components/remote_base/remote_base.h" | ||
|
||
namespace esphome { | ||
namespace gree { | ||
|
||
static const char *const TAG = "gree.climate"; | ||
|
||
void GreeClimate::set_model(Model model) { this->model_ = model; } | ||
|
||
void GreeClimate::transmit_state() { | ||
uint8_t remote_state[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00}; | ||
|
||
remote_state[0] = this->fan_speed_() | this->operation_mode_(); | ||
remote_state[1] = this->temperature_(); | ||
|
||
if (this->model_ == GREE_YAN) { | ||
remote_state[2] = 0x60; | ||
remote_state[3] = 0x50; | ||
remote_state[4] = this->vertical_swing_(); | ||
} | ||
|
||
if (this->model_ == GREE_YAC) { | ||
remote_state[4] |= (this->horizontal_swing_() << 4); | ||
} | ||
|
||
if (this->model_ == GREE_YAA || this->model_ == GREE_YAC) { | ||
remote_state[2] = 0x20; // bits 0..3 always 0000, bits 4..7 TURBO,LIGHT,HEALTH,X-FAN | ||
remote_state[3] = 0x50; // bits 4..7 always 0101 | ||
remote_state[6] = 0x20; // YAA1FB, FAA1FB1, YB1F2 bits 4..7 always 0010 | ||
|
||
if (this->vertical_swing_() == GREE_VDIR_SWING) { | ||
remote_state[0] |= (1 << 6); // Enable swing by setting bit 6 | ||
} else if (this->vertical_swing_() != GREE_VDIR_AUTO) { | ||
remote_state[5] = this->vertical_swing_(); | ||
} | ||
} | ||
|
||
// Calculate the checksum | ||
if (this->model_ == GREE_YAN) { | ||
remote_state[7] = ((remote_state[0] << 4) + (remote_state[1] << 4) + 0xC0); | ||
} else { | ||
remote_state[7] = | ||
((((remote_state[0] & 0x0F) + (remote_state[1] & 0x0F) + (remote_state[2] & 0x0F) + (remote_state[3] & 0x0F) + | ||
((remote_state[5] & 0xF0) >> 4) + ((remote_state[6] & 0xF0) >> 4) + ((remote_state[7] & 0xF0) >> 4) + 0x0A) & | ||
0x0F) | ||
<< 4) | | ||
(remote_state[7] & 0x0F); | ||
} | ||
|
||
auto transmit = this->transmitter_->transmit(); | ||
auto *data = transmit.get_data(); | ||
data->set_carrier_frequency(GREE_IR_FREQUENCY); | ||
|
||
data->mark(GREE_HEADER_MARK); | ||
data->space(GREE_HEADER_SPACE); | ||
|
||
for (int i = 0; i < 4; i++) { | ||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask | ||
data->mark(GREE_BIT_MARK); | ||
bool bit = remote_state[i] & mask; | ||
data->space(bit ? GREE_ONE_SPACE : GREE_ZERO_SPACE); | ||
} | ||
} | ||
|
||
data->mark(GREE_BIT_MARK); | ||
data->space(GREE_ZERO_SPACE); | ||
data->mark(GREE_BIT_MARK); | ||
data->space(GREE_ONE_SPACE); | ||
data->mark(GREE_BIT_MARK); | ||
data->space(GREE_ZERO_SPACE); | ||
|
||
data->mark(GREE_BIT_MARK); | ||
data->space(GREE_MESSAGE_SPACE); | ||
|
||
for (int i = 4; i < 8; i++) { | ||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask | ||
data->mark(GREE_BIT_MARK); | ||
bool bit = remote_state[i] & mask; | ||
data->space(bit ? GREE_ONE_SPACE : GREE_ZERO_SPACE); | ||
} | ||
} | ||
|
||
data->mark(GREE_BIT_MARK); | ||
data->space(0); | ||
|
||
transmit.perform(); | ||
} | ||
|
||
uint8_t GreeClimate::operation_mode_() { | ||
uint8_t operating_mode = GREE_MODE_ON; | ||
|
||
switch (this->mode) { | ||
case climate::CLIMATE_MODE_COOL: | ||
operating_mode |= GREE_MODE_COOL; | ||
break; | ||
case climate::CLIMATE_MODE_DRY: | ||
operating_mode |= GREE_MODE_DRY; | ||
break; | ||
case climate::CLIMATE_MODE_HEAT: | ||
operating_mode |= GREE_MODE_HEAT; | ||
break; | ||
case climate::CLIMATE_MODE_HEAT_COOL: | ||
operating_mode |= GREE_MODE_AUTO; | ||
break; | ||
case climate::CLIMATE_MODE_FAN_ONLY: | ||
operating_mode |= GREE_MODE_FAN; | ||
break; | ||
case climate::CLIMATE_MODE_OFF: | ||
default: | ||
operating_mode = GREE_MODE_OFF; | ||
break; | ||
} | ||
|
||
return operating_mode; | ||
} | ||
|
||
uint8_t GreeClimate::fan_speed_() { | ||
switch (this->fan_mode.value()) { | ||
case climate::CLIMATE_FAN_LOW: | ||
return GREE_FAN_1; | ||
case climate::CLIMATE_FAN_MEDIUM: | ||
return GREE_FAN_2; | ||
case climate::CLIMATE_FAN_HIGH: | ||
return GREE_FAN_3; | ||
case climate::CLIMATE_FAN_AUTO: | ||
default: | ||
return GREE_FAN_AUTO; | ||
} | ||
} | ||
|
||
uint8_t GreeClimate::horizontal_swing_() { | ||
switch (this->swing_mode) { | ||
case climate::CLIMATE_SWING_HORIZONTAL: | ||
case climate::CLIMATE_SWING_BOTH: | ||
return GREE_HDIR_SWING; | ||
default: | ||
return GREE_HDIR_MANUAL; | ||
} | ||
} | ||
|
||
uint8_t GreeClimate::vertical_swing_() { | ||
switch (this->swing_mode) { | ||
case climate::CLIMATE_SWING_VERTICAL: | ||
case climate::CLIMATE_SWING_BOTH: | ||
return GREE_VDIR_SWING; | ||
default: | ||
return GREE_VDIR_MANUAL; | ||
} | ||
} | ||
|
||
uint8_t GreeClimate::temperature_() { | ||
return (uint8_t) roundf(clamp<float>(this->target_temperature, GREE_TEMP_MIN, GREE_TEMP_MAX)); | ||
} | ||
|
||
} // namespace gree | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
#pragma once | ||
|
||
#include "esphome/components/climate_ir/climate_ir.h" | ||
|
||
namespace esphome { | ||
namespace gree { | ||
|
||
// Values for GREE IR Controllers | ||
// Temperature | ||
const uint8_t GREE_TEMP_MIN = 16; // Celsius | ||
const uint8_t GREE_TEMP_MAX = 30; // Celsius | ||
|
||
// Modes | ||
const uint8_t GREE_MODE_AUTO = 0x00; | ||
const uint8_t GREE_MODE_COOL = 0x01; | ||
const uint8_t GREE_MODE_HEAT = 0x04; | ||
const uint8_t GREE_MODE_DRY = 0x02; | ||
const uint8_t GREE_MODE_FAN = 0x03; | ||
|
||
const uint8_t GREE_MODE_OFF = 0x00; | ||
const uint8_t GREE_MODE_ON = 0x08; | ||
|
||
// Fan Speed | ||
const uint8_t GREE_FAN_AUTO = 0x00; | ||
const uint8_t GREE_FAN_1 = 0x10; | ||
const uint8_t GREE_FAN_2 = 0x20; | ||
const uint8_t GREE_FAN_3 = 0x30; | ||
const uint8_t GREE_FAN_TURBO = 0x80; | ||
|
||
// IR Transmission | ||
const uint32_t GREE_IR_FREQUENCY = 38000; | ||
const uint32_t GREE_HEADER_MARK = 9000; | ||
const uint32_t GREE_HEADER_SPACE = 4000; | ||
const uint32_t GREE_BIT_MARK = 620; | ||
const uint32_t GREE_ONE_SPACE = 1600; | ||
const uint32_t GREE_ZERO_SPACE = 540; | ||
const uint32_t GREE_MESSAGE_SPACE = 19000; | ||
|
||
// Timing specific for YAC features (I-Feel mode) | ||
const uint32_t GREE_YAC_HEADER_MARK = 6000; | ||
const uint32_t GREE_YAC_HEADER_SPACE = 3000; | ||
const uint32_t GREE_YAC_BIT_MARK = 650; | ||
|
||
// State Frame size | ||
const uint8_t GREE_STATE_FRAME_SIZE = 8; | ||
|
||
// Only available on YAN | ||
// Vertical air directions. Note that these cannot be set on all heat pumps | ||
const uint8_t GREE_VDIR_AUTO = 0x00; | ||
const uint8_t GREE_VDIR_MANUAL = 0x00; | ||
const uint8_t GREE_VDIR_SWING = 0x01; | ||
const uint8_t GREE_VDIR_UP = 0x02; | ||
const uint8_t GREE_VDIR_MUP = 0x03; | ||
const uint8_t GREE_VDIR_MIDDLE = 0x04; | ||
const uint8_t GREE_VDIR_MDOWN = 0x05; | ||
const uint8_t GREE_VDIR_DOWN = 0x06; | ||
|
||
// Only available on YAC | ||
// Horizontal air directions. Note that these cannot be set on all heat pumps | ||
const uint8_t GREE_HDIR_AUTO = 0x00; | ||
const uint8_t GREE_HDIR_MANUAL = 0x00; | ||
const uint8_t GREE_HDIR_SWING = 0x01; | ||
const uint8_t GREE_HDIR_LEFT = 0x02; | ||
const uint8_t GREE_HDIR_MLEFT = 0x03; | ||
const uint8_t GREE_HDIR_MIDDLE = 0x04; | ||
const uint8_t GREE_HDIR_MRIGHT = 0x05; | ||
const uint8_t GREE_HDIR_RIGHT = 0x06; | ||
|
||
// Model codes | ||
enum Model { GREE_GENERIC, GREE_YAN, GREE_YAA, GREE_YAC }; | ||
|
||
class GreeClimate : public climate_ir::ClimateIR { | ||
public: | ||
GreeClimate() | ||
: climate_ir::ClimateIR(GREE_TEMP_MIN, GREE_TEMP_MAX, 1.0f, true, true, | ||
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, | ||
climate::CLIMATE_FAN_HIGH}, | ||
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, | ||
climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_BOTH}) {} | ||
|
||
void set_model(Model model); | ||
|
||
protected: | ||
// Transmit via IR the state of this climate controller. | ||
void transmit_state() override; | ||
|
||
uint8_t operation_mode_(); | ||
uint8_t fan_speed_(); | ||
uint8_t horizontal_swing_(); | ||
uint8_t vertical_swing_(); | ||
uint8_t temperature_(); | ||
|
||
Model model_{}; | ||
}; | ||
|
||
} // namespace gree | ||
} // namespace esphome |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters