mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +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/gpio/* @esphome/core | ||||||
| esphome/components/gps/* @coogle | esphome/components/gps/* @coogle | ||||||
| esphome/components/graph/* @synco | esphome/components/graph/* @synco | ||||||
|  | esphome/components/gree/* @orestismers | ||||||
| esphome/components/grove_tb6612fng/* @max246 | esphome/components/grove_tb6612fng/* @max246 | ||||||
| esphome/components/growatt_solar/* @leeuwte | esphome/components/growatt_solar/* @leeuwte | ||||||
| esphome/components/haier/* @paveldn | 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 |     heat_mode: extended | ||||||
|   - platform: whynter |   - platform: whynter | ||||||
|     name: Whynter |     name: Whynter | ||||||
|  |   - platform: gree | ||||||
|  |     name: GREE | ||||||
|  |     model: generic | ||||||
|   - platform: zhlt01 |   - platform: zhlt01 | ||||||
|     name: ZH/LT-01 Climate |     name: ZH/LT-01 Climate | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user