mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add LG Climate IR (#1097)
* Add LG Climate ir * Add LG Climate IR * Add LG Climate IR * Add LG Climate IR * Add LG Climate IR * Add LG Climate IR * Add LG Climate IR Co-authored-by: Sangheon Heo <square99@SangHeonui-Macmini.local>
This commit is contained in:
		
							
								
								
									
										0
									
								
								esphome/components/climate_ir_lg/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/climate_ir_lg/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										18
									
								
								esphome/components/climate_ir_lg/climate.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								esphome/components/climate_ir_lg/climate.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import climate_ir | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  | AUTO_LOAD = ['climate_ir'] | ||||||
|  |  | ||||||
|  | climate_ir_lg_ns = cg.esphome_ns.namespace('climate_ir_lg') | ||||||
|  | LgIrClimate = climate_ir_lg_ns.class_('LgIrClimate', climate_ir.ClimateIR) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ | ||||||
|  |     cv.GenerateID(): cv.declare_id(LgIrClimate), | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     yield climate_ir.register_climate_ir(var, config) | ||||||
							
								
								
									
										204
									
								
								esphome/components/climate_ir_lg/climate_ir_lg.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								esphome/components/climate_ir_lg/climate_ir_lg.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,204 @@ | |||||||
|  | #include "climate_ir_lg.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace climate_ir_lg { | ||||||
|  |  | ||||||
|  | static const char *TAG = "climate.climate_ir_lg"; | ||||||
|  |  | ||||||
|  | const uint32_t COMMAND_ON = 0x00000; | ||||||
|  | const uint32_t COMMAND_ON_AI = 0x03000; | ||||||
|  | const uint32_t COMMAND_COOL = 0x08000; | ||||||
|  | const uint32_t COMMAND_OFF = 0xC0000; | ||||||
|  | const uint32_t COMMAND_SWING = 0x10000; | ||||||
|  | // On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore. | ||||||
|  | const uint32_t COMMAND_AUTO = 0x0B000; | ||||||
|  | const uint32_t COMMAND_DRY_FAN = 0x09000; | ||||||
|  |  | ||||||
|  | const uint32_t COMMAND_MASK = 0xFF000; | ||||||
|  |  | ||||||
|  | const uint32_t FAN_MASK = 0xF0; | ||||||
|  | const uint32_t FAN_AUTO = 0x50; | ||||||
|  | const uint32_t FAN_MIN = 0x00; | ||||||
|  | const uint32_t FAN_MED = 0x20; | ||||||
|  | const uint32_t FAN_MAX = 0x40; | ||||||
|  |  | ||||||
|  | // Temperature | ||||||
|  | const uint8_t TEMP_RANGE = TEMP_MAX - TEMP_MIN + 1; | ||||||
|  | const uint32_t TEMP_MASK = 0XF00; | ||||||
|  | const uint32_t TEMP_SHIFT = 8; | ||||||
|  |  | ||||||
|  | // Constants | ||||||
|  | static const uint32_t HEADER_HIGH_US = 8000; | ||||||
|  | static const uint32_t HEADER_LOW_US = 4000; | ||||||
|  | static const uint32_t BIT_HIGH_US = 600; | ||||||
|  | static const uint32_t BIT_ONE_LOW_US = 1600; | ||||||
|  | static const uint32_t BIT_ZERO_LOW_US = 550; | ||||||
|  |  | ||||||
|  | const uint16_t BITS = 28; | ||||||
|  |  | ||||||
|  | void LgIrClimate::transmit_state() { | ||||||
|  |   uint32_t remote_state = 0x8800000; | ||||||
|  |  | ||||||
|  |   // ESP_LOGD(TAG, "climate_lg_ir mode_before_ code: 0x%02X", modeBefore_); | ||||||
|  |   if (send_swing_cmd_) { | ||||||
|  |     send_swing_cmd_ = false; | ||||||
|  |     remote_state |= COMMAND_SWING; | ||||||
|  |   } else { | ||||||
|  |     if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode == climate::CLIMATE_MODE_AUTO) { | ||||||
|  |       remote_state |= COMMAND_ON_AI; | ||||||
|  |     } else if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode != climate::CLIMATE_MODE_OFF) { | ||||||
|  |       remote_state |= COMMAND_ON; | ||||||
|  |       this->mode = climate::CLIMATE_MODE_COOL; | ||||||
|  |     } else { | ||||||
|  |       switch (this->mode) { | ||||||
|  |         case climate::CLIMATE_MODE_COOL: | ||||||
|  |           remote_state |= COMMAND_COOL; | ||||||
|  |           break; | ||||||
|  |         case climate::CLIMATE_MODE_AUTO: | ||||||
|  |           remote_state |= COMMAND_AUTO; | ||||||
|  |           break; | ||||||
|  |         case climate::CLIMATE_MODE_DRY: | ||||||
|  |           remote_state |= COMMAND_DRY_FAN; | ||||||
|  |           break; | ||||||
|  |         case climate::CLIMATE_MODE_OFF: | ||||||
|  |         default: | ||||||
|  |           remote_state |= COMMAND_OFF; | ||||||
|  |           break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     mode_before_ = this->mode; | ||||||
|  |  | ||||||
|  |     ESP_LOGD(TAG, "climate_lg_ir mode code: 0x%02X", this->mode); | ||||||
|  |  | ||||||
|  |     if (this->mode == climate::CLIMATE_MODE_OFF) { | ||||||
|  |       remote_state |= FAN_AUTO; | ||||||
|  |     } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) { | ||||||
|  |       switch (this->fan_mode) { | ||||||
|  |         case climate::CLIMATE_FAN_HIGH: | ||||||
|  |           remote_state |= FAN_MAX; | ||||||
|  |           break; | ||||||
|  |         case climate::CLIMATE_FAN_MEDIUM: | ||||||
|  |           remote_state |= FAN_MED; | ||||||
|  |           break; | ||||||
|  |         case climate::CLIMATE_FAN_LOW: | ||||||
|  |           remote_state |= FAN_MIN; | ||||||
|  |           break; | ||||||
|  |         case climate::CLIMATE_FAN_AUTO: | ||||||
|  |         default: | ||||||
|  |           remote_state |= FAN_AUTO; | ||||||
|  |           break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (this->mode == climate::CLIMATE_MODE_AUTO) { | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_AUTO; | ||||||
|  |       // remote_state |= FAN_MODE_AUTO_DRY; | ||||||
|  |     } | ||||||
|  |     if (this->mode == climate::CLIMATE_MODE_COOL) { | ||||||
|  |       auto temp = (uint8_t) roundf(clamp(this->target_temperature, TEMP_MIN, TEMP_MAX)); | ||||||
|  |       remote_state |= ((temp - 15) << TEMP_SHIFT); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   transmit_(remote_state); | ||||||
|  |   this->publish_state(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) { | ||||||
|  |   uint8_t nbits = 0; | ||||||
|  |   uint32_t remote_state = 0; | ||||||
|  |  | ||||||
|  |   if (!data.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   for (nbits = 0; nbits < 32; nbits++) { | ||||||
|  |     if (data.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { | ||||||
|  |       remote_state = (remote_state << 1) | 1; | ||||||
|  |     } else if (data.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { | ||||||
|  |       remote_state = (remote_state << 1) | 0; | ||||||
|  |     } else if (nbits == BITS) { | ||||||
|  |       break; | ||||||
|  |     } else { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGD(TAG, "Decoded 0x%02X", remote_state); | ||||||
|  |   if ((remote_state & 0xFF00000) != 0x8800000) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   if ((remote_state & COMMAND_MASK) == COMMAND_ON) { | ||||||
|  |     this->mode = climate::CLIMATE_MODE_COOL; | ||||||
|  |   } else if ((remote_state & COMMAND_MASK) == COMMAND_ON_AI) { | ||||||
|  |     this->mode = climate::CLIMATE_MODE_AUTO; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if ((remote_state & COMMAND_MASK) == COMMAND_OFF) { | ||||||
|  |     this->mode = climate::CLIMATE_MODE_OFF; | ||||||
|  |   } else if ((remote_state & COMMAND_MASK) == COMMAND_SWING) { | ||||||
|  |     this->swing_mode = | ||||||
|  |         this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF; | ||||||
|  |   } else { | ||||||
|  |     if ((remote_state & COMMAND_MASK) == COMMAND_AUTO) | ||||||
|  |       this->mode = climate::CLIMATE_MODE_AUTO; | ||||||
|  |     else if ((remote_state & COMMAND_MASK) == COMMAND_DRY_FAN) { | ||||||
|  |       this->mode = climate::CLIMATE_MODE_DRY; | ||||||
|  |     } else { | ||||||
|  |       this->mode = climate::CLIMATE_MODE_COOL; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Temperature | ||||||
|  |   if (this->mode == climate::CLIMATE_MODE_COOL) | ||||||
|  |     this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15; | ||||||
|  |  | ||||||
|  |   // Fan Speed | ||||||
|  |   if (this->mode == climate::CLIMATE_MODE_AUTO) { | ||||||
|  |     this->fan_mode = climate::CLIMATE_FAN_AUTO; | ||||||
|  |   } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) { | ||||||
|  |     if ((remote_state & FAN_MASK) == FAN_AUTO) | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_AUTO; | ||||||
|  |     else if ((remote_state & FAN_MASK) == FAN_MIN) | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_LOW; | ||||||
|  |     else if ((remote_state & FAN_MASK) == FAN_MED) | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_MEDIUM; | ||||||
|  |     else if ((remote_state & FAN_MASK) == FAN_MAX) | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_HIGH; | ||||||
|  |   } | ||||||
|  |   this->publish_state(); | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | void LgIrClimate::transmit_(uint32_t value) { | ||||||
|  |   calc_checksum_(value); | ||||||
|  |   ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02X", value); | ||||||
|  |  | ||||||
|  |   auto transmit = this->transmitter_->transmit(); | ||||||
|  |   auto data = transmit.get_data(); | ||||||
|  |  | ||||||
|  |   data->set_carrier_frequency(38000); | ||||||
|  |   data->reserve(2 + BITS * 2u); | ||||||
|  |  | ||||||
|  |   data->item(HEADER_HIGH_US, HEADER_LOW_US); | ||||||
|  |  | ||||||
|  |   for (uint32_t mask = 1UL << (BITS - 1); mask != 0; mask >>= 1) { | ||||||
|  |     if (value & mask) | ||||||
|  |       data->item(BIT_HIGH_US, BIT_ONE_LOW_US); | ||||||
|  |     else | ||||||
|  |       data->item(BIT_HIGH_US, BIT_ZERO_LOW_US); | ||||||
|  |   } | ||||||
|  |   data->mark(BIT_HIGH_US); | ||||||
|  |   transmit.perform(); | ||||||
|  | } | ||||||
|  | void LgIrClimate::calc_checksum_(uint32_t &value) { | ||||||
|  |   uint32_t mask = 0xF; | ||||||
|  |   uint32_t sum = 0; | ||||||
|  |   for (uint8_t i = 1; i < 8; i++) { | ||||||
|  |     sum += (value & (mask << (i * 4))) >> (i * 4); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   value |= (sum & mask); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace climate_ir_lg | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										44
									
								
								esphome/components/climate_ir_lg/climate_ir_lg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/climate_ir_lg/climate_ir_lg.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/climate_ir/climate_ir.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace climate_ir_lg { | ||||||
|  |  | ||||||
|  | // Temperature | ||||||
|  | const uint8_t TEMP_MIN = 18;  // Celsius | ||||||
|  | const uint8_t TEMP_MAX = 30;  // Celsius | ||||||
|  |  | ||||||
|  | class LgIrClimate : public climate_ir::ClimateIR { | ||||||
|  |  public: | ||||||
|  |   LgIrClimate() | ||||||
|  |       : climate_ir::ClimateIR(TEMP_MIN, TEMP_MAX, 1.0f, true, false, | ||||||
|  |                               {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, | ||||||
|  |                                climate::CLIMATE_FAN_HIGH}, | ||||||
|  |                               {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {} | ||||||
|  |  | ||||||
|  |   /// Override control to change settings of the climate device. | ||||||
|  |   void control(const climate::ClimateCall &call) override { | ||||||
|  |     send_swing_cmd_ = call.get_swing_mode().has_value(); | ||||||
|  |     // swing resets after unit powered off | ||||||
|  |     if (call.get_mode().has_value() && *call.get_mode() == climate::CLIMATE_MODE_OFF) | ||||||
|  |       this->swing_mode = climate::CLIMATE_SWING_OFF; | ||||||
|  |     climate_ir::ClimateIR::control(call); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   /// Transmit via IR the state of this climate controller. | ||||||
|  |   void transmit_state() override; | ||||||
|  |   /// Handle received IR Buffer | ||||||
|  |   bool on_receive(remote_base::RemoteReceiveData data) override; | ||||||
|  |  | ||||||
|  |   bool send_swing_cmd_{false}; | ||||||
|  |  | ||||||
|  |   void calc_checksum_(uint32_t &value); | ||||||
|  |   void transmit_(uint32_t value); | ||||||
|  |  | ||||||
|  |   climate::ClimateMode mode_before_{climate::CLIMATE_MODE_OFF}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace climate_ir_lg | ||||||
|  | }  // namespace esphome | ||||||
| @@ -1277,6 +1277,8 @@ climate: | |||||||
|     name: Mitsubishi |     name: Mitsubishi | ||||||
|   - platform: whirlpool |   - platform: whirlpool | ||||||
|     name: Whirlpool Climate |     name: Whirlpool Climate | ||||||
|  |   - platform: climate_ir_lg | ||||||
|  |     name: LG Climate | ||||||
|  |  | ||||||
| switch: | switch: | ||||||
|   - platform: gpio |   - platform: gpio | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user