mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Feat: Add GREE climateir component (#4464)
Co-authored-by: orestismers <33354671+orestismers@users.noreply.github.com> Co-authored-by: Orestes Mersinias <orestis.mers@gmail.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -110,6 +110,7 @@ esphome/components/gp8403/* @jesserockz | ||||
| esphome/components/gpio/* @esphome/core | ||||
| esphome/components/gps/* @coogle | ||||
| esphome/components/graph/* @synco | ||||
| esphome/components/gree/* @orestismers | ||||
| esphome/components/grove_tb6612fng/* @max246 | ||||
| esphome/components/growatt_solar/* @leeuwte | ||||
| esphome/components/haier/* @paveldn | ||||
|   | ||||
							
								
								
									
										0
									
								
								esphome/components/gree/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/gree/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										33
									
								
								esphome/components/gree/climate.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								esphome/components/gree/climate.py
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
							
								
								
									
										157
									
								
								esphome/components/gree/gree.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								esphome/components/gree/gree.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										97
									
								
								esphome/components/gree/gree.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								esphome/components/gree/gree.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -2297,6 +2297,9 @@ climate: | ||||
|     heat_mode: extended | ||||
|   - platform: whynter | ||||
|     name: Whynter | ||||
|   - platform: gree | ||||
|     name: GREE | ||||
|     model: generic | ||||
|   - platform: zhlt01 | ||||
|     name: ZH/LT-01 Climate | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user