mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add ZH/LT-01 climate component with IR receiver option (#4333)
Co-authored-by: Chris Feenstra <chris@cfeenstra.nl> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -345,4 +345,5 @@ esphome/components/xiaomi_mhoc401/* @vevsvevs | |||||||
| esphome/components/xiaomi_rtcgq02lm/* @jesserockz | esphome/components/xiaomi_rtcgq02lm/* @jesserockz | ||||||
| esphome/components/xl9535/* @mreditor97 | esphome/components/xl9535/* @mreditor97 | ||||||
| esphome/components/xpt2046/* @nielsnl68 @numo68 | esphome/components/xpt2046/* @nielsnl68 @numo68 | ||||||
|  | esphome/components/zhlt01/* @cfeenstra1024 | ||||||
| esphome/components/zio_ultrasonic/* @kahrendt | esphome/components/zio_ultrasonic/* @kahrendt | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								esphome/components/zhlt01/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/zhlt01/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										19
									
								
								esphome/components/zhlt01/climate.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								esphome/components/zhlt01/climate.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | 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"] | ||||||
|  | CODEOWNERS = ["@cfeenstra1024"] | ||||||
|  |  | ||||||
|  | zhlt01_ns = cg.esphome_ns.namespace("zhlt01") | ||||||
|  | ZHLT01Climate = zhlt01_ns.class_("ZHLT01Climate", climate_ir.ClimateIR) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( | ||||||
|  |     {cv.GenerateID(): cv.declare_id(ZHLT01Climate)} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await climate_ir.register_climate_ir(var, config) | ||||||
							
								
								
									
										238
									
								
								esphome/components/zhlt01/zhlt01.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								esphome/components/zhlt01/zhlt01.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,238 @@ | |||||||
|  | #include "zhlt01.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace zhlt01 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "zhlt01.climate"; | ||||||
|  |  | ||||||
|  | void ZHLT01Climate::transmit_state() { | ||||||
|  |   uint8_t ir_message[12] = {0}; | ||||||
|  |  | ||||||
|  |   // Byte 1 : Timer | ||||||
|  |   ir_message[1] = 0x00;  // Timer off | ||||||
|  |  | ||||||
|  |   // Byte 3 : Turbo mode | ||||||
|  |   if (this->preset.value() == climate::CLIMATE_PRESET_BOOST) { | ||||||
|  |     ir_message[3] = AC1_FAN_TURBO; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Byte 5 : Last pressed button | ||||||
|  |   ir_message[5] = 0x00;  // fixed as power button | ||||||
|  |  | ||||||
|  |   // Byte 7 : Power | Swing | Fan | ||||||
|  |   // -- Power | ||||||
|  |   if (this->mode == climate::CLIMATE_MODE_OFF) { | ||||||
|  |     ir_message[7] = AC1_POWER_OFF; | ||||||
|  |   } else { | ||||||
|  |     ir_message[7] = AC1_POWER_ON; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // -- Swing | ||||||
|  |   switch (this->swing_mode) { | ||||||
|  |     case climate::CLIMATE_SWING_OFF: | ||||||
|  |       ir_message[7] |= AC1_HDIR_FIXED | AC1_VDIR_FIXED; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_SWING_HORIZONTAL: | ||||||
|  |       ir_message[7] |= AC1_HDIR_SWING | AC1_VDIR_FIXED; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_SWING_VERTICAL: | ||||||
|  |       ir_message[7] |= AC1_HDIR_FIXED | AC1_VDIR_SWING; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_SWING_BOTH: | ||||||
|  |       ir_message[7] |= AC1_HDIR_SWING | AC1_VDIR_SWING; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // -- Fan | ||||||
|  |   switch (this->preset.value()) { | ||||||
|  |     case climate::CLIMATE_PRESET_BOOST: | ||||||
|  |       ir_message[7] |= AC1_FAN3; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_PRESET_SLEEP: | ||||||
|  |       ir_message[7] |= AC1_FAN_SILENT; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       switch (this->fan_mode.value()) { | ||||||
|  |         case climate::CLIMATE_FAN_LOW: | ||||||
|  |           ir_message[7] |= AC1_FAN1; | ||||||
|  |           break; | ||||||
|  |         case climate::CLIMATE_FAN_MEDIUM: | ||||||
|  |           ir_message[7] |= AC1_FAN2; | ||||||
|  |           break; | ||||||
|  |         case climate::CLIMATE_FAN_HIGH: | ||||||
|  |           ir_message[7] |= AC1_FAN3; | ||||||
|  |           break; | ||||||
|  |         case climate::CLIMATE_FAN_AUTO: | ||||||
|  |           ir_message[7] |= AC1_FAN_AUTO; | ||||||
|  |           break; | ||||||
|  |         default: | ||||||
|  |           break; | ||||||
|  |       } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Byte 9 : AC Mode | Temperature | ||||||
|  |   // -- AC Mode | ||||||
|  |   switch (this->mode) { | ||||||
|  |     case climate::CLIMATE_MODE_AUTO: | ||||||
|  |     case climate::CLIMATE_MODE_HEAT_COOL: | ||||||
|  |       ir_message[9] = AC1_MODE_AUTO; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_COOL: | ||||||
|  |       ir_message[9] = AC1_MODE_COOL; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_HEAT: | ||||||
|  |       ir_message[9] = AC1_MODE_HEAT; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_DRY: | ||||||
|  |       ir_message[9] = AC1_MODE_DRY; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_FAN_ONLY: | ||||||
|  |       ir_message[9] = AC1_MODE_FAN; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // -- Temperature | ||||||
|  |   ir_message[9] |= (uint8_t) (this->target_temperature - 16.0f); | ||||||
|  |  | ||||||
|  |   // Byte 11 : Remote control ID | ||||||
|  |   ir_message[11] = 0xD5; | ||||||
|  |  | ||||||
|  |   // Set checksum bytes | ||||||
|  |   for (int i = 0; i < 12; i += 2) { | ||||||
|  |     ir_message[i] = ~ir_message[i + 1]; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Send the code | ||||||
|  |   auto transmit = this->transmitter_->transmit(); | ||||||
|  |   auto *data = transmit.get_data(); | ||||||
|  |  | ||||||
|  |   data->set_carrier_frequency(38000);  // 38 kHz PWM | ||||||
|  |  | ||||||
|  |   // Header | ||||||
|  |   data->mark(AC1_HDR_MARK); | ||||||
|  |   data->space(AC1_HDR_SPACE); | ||||||
|  |  | ||||||
|  |   // Data | ||||||
|  |   for (uint8_t i : ir_message) { | ||||||
|  |     for (uint8_t j = 0; j < 8; j++) { | ||||||
|  |       data->mark(AC1_BIT_MARK); | ||||||
|  |       bool bit = i & (1 << j); | ||||||
|  |       data->space(bit ? AC1_ONE_SPACE : AC1_ZERO_SPACE); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Footer | ||||||
|  |   data->mark(AC1_BIT_MARK); | ||||||
|  |   data->space(0); | ||||||
|  |  | ||||||
|  |   transmit.perform(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ZHLT01Climate::on_receive(remote_base::RemoteReceiveData data) { | ||||||
|  |   // Validate header | ||||||
|  |   if (!data.expect_item(AC1_HDR_MARK, AC1_HDR_SPACE)) { | ||||||
|  |     ESP_LOGV(TAG, "Header fail"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Decode IR message | ||||||
|  |   uint8_t ir_message[12] = {0}; | ||||||
|  |   // Read all bytes | ||||||
|  |   for (int i = 0; i < 12; i++) { | ||||||
|  |     // Read bit | ||||||
|  |     for (int j = 0; j < 8; j++) { | ||||||
|  |       if (data.expect_item(AC1_BIT_MARK, AC1_ONE_SPACE)) { | ||||||
|  |         ir_message[i] |= 1 << j; | ||||||
|  |       } else if (!data.expect_item(AC1_BIT_MARK, AC1_ZERO_SPACE)) { | ||||||
|  |         ESP_LOGV(TAG, "Byte %d bit %d fail", i, j); | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     ESP_LOGVV(TAG, "Byte %d %02X", i, ir_message[i]); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Validate footer | ||||||
|  |   if (!data.expect_mark(AC1_BIT_MARK)) { | ||||||
|  |     ESP_LOGV(TAG, "Footer fail"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Validate checksum | ||||||
|  |   for (int i = 0; i < 12; i += 2) { | ||||||
|  |     if (ir_message[i] != (uint8_t) (~ir_message[i + 1])) { | ||||||
|  |       ESP_LOGV(TAG, "Byte %d checksum incorrect (%02X != %02X)", i, ir_message[i], (uint8_t) (~ir_message[i + 1])); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Validate remote control ID | ||||||
|  |   if (ir_message[11] != 0xD5) { | ||||||
|  |     ESP_LOGV(TAG, "Invalid remote control ID"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // All is good to go | ||||||
|  |  | ||||||
|  |   if ((ir_message[7] & AC1_POWER_ON) == 0) { | ||||||
|  |     this->mode = climate::CLIMATE_MODE_OFF; | ||||||
|  |   } else { | ||||||
|  |     // Vertical swing | ||||||
|  |     if ((ir_message[7] & 0x0C) == AC1_VDIR_FIXED) { | ||||||
|  |       if ((ir_message[7] & 0x10) == AC1_HDIR_FIXED) { | ||||||
|  |         this->swing_mode = climate::CLIMATE_SWING_OFF; | ||||||
|  |       } else { | ||||||
|  |         this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       if ((ir_message[7] & 0x10) == AC1_HDIR_FIXED) { | ||||||
|  |         this->swing_mode = climate::CLIMATE_SWING_VERTICAL; | ||||||
|  |       } else { | ||||||
|  |         this->swing_mode = climate::CLIMATE_SWING_BOTH; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Preset + Fan speed | ||||||
|  |     if ((ir_message[3] & AC1_FAN_TURBO) == AC1_FAN_TURBO) { | ||||||
|  |       this->preset = climate::CLIMATE_PRESET_BOOST; | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_HIGH; | ||||||
|  |     } else if ((ir_message[7] & 0xE1) == AC1_FAN_SILENT) { | ||||||
|  |       this->preset = climate::CLIMATE_PRESET_SLEEP; | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_LOW; | ||||||
|  |     } else if ((ir_message[7] & 0xE1) == AC1_FAN_AUTO) { | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_AUTO; | ||||||
|  |     } else if ((ir_message[7] & 0xE1) == AC1_FAN1) { | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_LOW; | ||||||
|  |     } else if ((ir_message[7] & 0xE1) == AC1_FAN2) { | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_MEDIUM; | ||||||
|  |     } else if ((ir_message[7] & 0xE1) == AC1_FAN3) { | ||||||
|  |       this->fan_mode = climate::CLIMATE_FAN_HIGH; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // AC Mode | ||||||
|  |     if ((ir_message[9] & 0xE0) == AC1_MODE_COOL) { | ||||||
|  |       this->mode = climate::CLIMATE_MODE_COOL; | ||||||
|  |     } else if ((ir_message[9] & 0xE0) == AC1_MODE_HEAT) { | ||||||
|  |       this->mode = climate::CLIMATE_MODE_HEAT; | ||||||
|  |     } else if ((ir_message[9] & 0xE0) == AC1_MODE_DRY) { | ||||||
|  |       this->mode = climate::CLIMATE_MODE_DRY; | ||||||
|  |     } else if ((ir_message[9] & 0xE0) == AC1_MODE_FAN) { | ||||||
|  |       this->mode = climate::CLIMATE_MODE_FAN_ONLY; | ||||||
|  |     } else { | ||||||
|  |       this->mode = climate::CLIMATE_MODE_AUTO; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Taregt Temperature | ||||||
|  |     this->target_temperature = (ir_message[9] & 0x1F) + 16.0f; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   this->publish_state(); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace zhlt01 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										167
									
								
								esphome/components/zhlt01/zhlt01.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								esphome/components/zhlt01/zhlt01.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/components/climate_ir/climate_ir.h" | ||||||
|  |  | ||||||
|  | /*********************************************************************************** | ||||||
|  |  * SOURCE | ||||||
|  |  *********************************************************************************** | ||||||
|  |  * The IR codes and the functional description below were taken from | ||||||
|  |  * 'arduino-heatpumpir/ZHLT01HeatpumpIR.h' as can be found on GitHub | ||||||
|  |  * https://github.com/ToniA/arduino-heatpumpir/blob/master/ZHLT01HeatpumpIR.h | ||||||
|  |  * | ||||||
|  |  ************************************************************************************ | ||||||
|  |  *  Airconditional remote control encoder for: | ||||||
|  |  * | ||||||
|  |  *  ZH/LT-01 Remote control https://www.google.com/search?q=zh/lt-01 | ||||||
|  |  * | ||||||
|  |  *  The ZH/LT-01 remote control is used for many locally branded Split | ||||||
|  |  *  airconditioners, so it is better to name this protocol by the name of the | ||||||
|  |  *  REMOTE rather then the name of the Airconditioner. For this project I used | ||||||
|  |  *  a 2014 model Eurom-airconditioner, which is Dutch-branded and sold in | ||||||
|  |  *  the Netherlands at Hornbach. | ||||||
|  |  * | ||||||
|  |  * For airco-brands: | ||||||
|  |  *  Eurom | ||||||
|  |  *  Chigo | ||||||
|  |  *  Tristar | ||||||
|  |  *  Tecnomaster | ||||||
|  |  *  Elgin | ||||||
|  |  *  Geant | ||||||
|  |  *  Tekno | ||||||
|  |  *  Topair | ||||||
|  |  *  Proma | ||||||
|  |  *  Sumikura | ||||||
|  |  *  JBS | ||||||
|  |  *  Turbo Air | ||||||
|  |  *  Nakatomy | ||||||
|  |  *  Celestial Air | ||||||
|  |  *  Ager | ||||||
|  |  *  Blueway | ||||||
|  |  *  Airlux | ||||||
|  |  *  Etc. | ||||||
|  |  * | ||||||
|  |  *********************************************************************************** | ||||||
|  |  *  SUMMARY FUNCTIONAL DESCRIPTION | ||||||
|  |  *********************************************************************************** | ||||||
|  |  *  The remote sends a 12 Byte message which contains all possible settings every | ||||||
|  |  *  time. | ||||||
|  |  * | ||||||
|  |  *  Byte 11 (and 10) contain the remote control identifier and are always 0xD5 and | ||||||
|  |  *  0x2A respectively for the ZH/LT-01 remote control. | ||||||
|  |  *  Every UNeven Byte (01,03,05,07 and 09) holds command data | ||||||
|  |  *  Every EVEN Byte (00,02,04,06,08 and 10) holds a checksum of the corresponding | ||||||
|  |  *  command-, or identifier-byte by _inverting_ the bits, for example: | ||||||
|  |  * | ||||||
|  |  *  The identifier byte[11] = 0xD5 = B1101 0101 | ||||||
|  |  *  The checksum byte[10]   = 0x2A = B0010 1010 | ||||||
|  |  * | ||||||
|  |  *  So, you can check the message by: | ||||||
|  |  *  - inverting the bits of the checksum byte with the corresponding command-, or | ||||||
|  |  *    identifier byte, they should be the same, or | ||||||
|  |  *  - Summing up the checksum byte and the corresponding command-, or identifier byte, | ||||||
|  |  *    they should always add up to 0xFF = B11111111 = 255 | ||||||
|  |  * | ||||||
|  |  *  Control bytes: | ||||||
|  |  *  [01] - Timer (1-24 hours, Off) | ||||||
|  |  *         Time is hardcoded to OFF | ||||||
|  |  * | ||||||
|  |  *  [03] - LAMP ON/OFF, TURBO ON/OFF, HOLD ON/OFF | ||||||
|  |  *         Lamp and Hold are hardcoded to OFF | ||||||
|  |  *         Turbo is used for the BOOST preset | ||||||
|  |  * | ||||||
|  |  *  [05] - Indicates which button the user _pressed_ on the remote control | ||||||
|  |  *         Hardcoded to POWER-button | ||||||
|  |  * | ||||||
|  |  *  [07] - POWER ON/OFF, FAN AUTO/3/2/1, SLEEP ON/OFF, AIRFLOW ON/OFF, | ||||||
|  |  *         VERTICAL SWING/WIND/FIXED | ||||||
|  |  *         SLEEP is used for preset SLEEP | ||||||
|  |  *         Vertical Swing supports Fixed, Swing and "Wind". The Wind option | ||||||
|  |  *         is ignored in this implementation | ||||||
|  |  * | ||||||
|  |  *  [09] - MODE AUTO/COOL/VENT/DRY/HEAT, TEMPERATURE (16 - 32°C) | ||||||
|  |  * | ||||||
|  |  ***********************************************************************************/ | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace zhlt01 { | ||||||
|  |  | ||||||
|  | /******************************************************************************** | ||||||
|  |  *  TIMINGS | ||||||
|  |  *  Space:        Not used | ||||||
|  |  *  Header Mark:  6100 us | ||||||
|  |  *  Header Space: 7400 us | ||||||
|  |  *  Bit Mark:      500 us | ||||||
|  |  *  Zero Space:    600 us | ||||||
|  |  *  One Space:    1800 us | ||||||
|  |  * | ||||||
|  |  * Note : These timings are slightly different than those of ZHLT01HeatpumpIR | ||||||
|  |  *        The values below were measured by taking the average of 2 different | ||||||
|  |  *        remote controls each sending 10 commands | ||||||
|  |  *******************************************************************************/ | ||||||
|  | static const uint32_t AC1_HDR_MARK = 6100; | ||||||
|  | static const uint32_t AC1_HDR_SPACE = 7400; | ||||||
|  | static const uint32_t AC1_BIT_MARK = 500; | ||||||
|  | static const uint32_t AC1_ZERO_SPACE = 600; | ||||||
|  | static const uint32_t AC1_ONE_SPACE = 1800; | ||||||
|  |  | ||||||
|  | /******************************************************************************** | ||||||
|  |  * | ||||||
|  |  * ZHLT01 codes | ||||||
|  |  * | ||||||
|  |  *******************************************************************************/ | ||||||
|  |  | ||||||
|  | // Power | ||||||
|  | static const uint8_t AC1_POWER_OFF = 0x00; | ||||||
|  | static const uint8_t AC1_POWER_ON = 0x02; | ||||||
|  |  | ||||||
|  | // Operating Modes | ||||||
|  | static const uint8_t AC1_MODE_AUTO = 0x00; | ||||||
|  | static const uint8_t AC1_MODE_COOL = 0x20; | ||||||
|  | static const uint8_t AC1_MODE_DRY = 0x40; | ||||||
|  | static const uint8_t AC1_MODE_FAN = 0x60; | ||||||
|  | static const uint8_t AC1_MODE_HEAT = 0x80; | ||||||
|  |  | ||||||
|  | // Fan control | ||||||
|  | static const uint8_t AC1_FAN_AUTO = 0x00; | ||||||
|  | static const uint8_t AC1_FAN_SILENT = 0x01; | ||||||
|  | static const uint8_t AC1_FAN1 = 0x60; | ||||||
|  | static const uint8_t AC1_FAN2 = 0x40; | ||||||
|  | static const uint8_t AC1_FAN3 = 0x20; | ||||||
|  | static const uint8_t AC1_FAN_TURBO = 0x08; | ||||||
|  |  | ||||||
|  | // Vertical Swing | ||||||
|  | static const uint8_t AC1_VDIR_WIND = 0x00;   // "Natural Wind", ignore | ||||||
|  | static const uint8_t AC1_VDIR_SWING = 0x04;  // Swing | ||||||
|  | static const uint8_t AC1_VDIR_FIXED = 0x08;  // Fixed | ||||||
|  |  | ||||||
|  | // Horizontal Swing | ||||||
|  | static const uint8_t AC1_HDIR_SWING = 0x00;  // Swing | ||||||
|  | static const uint8_t AC1_HDIR_FIXED = 0x10;  // Fixed | ||||||
|  |  | ||||||
|  | // Temperature range | ||||||
|  | static const float AC1_TEMP_MIN = 16.0f; | ||||||
|  | static const float AC1_TEMP_MAX = 32.0f; | ||||||
|  | static const float AC1_TEMP_INC = 1.0f; | ||||||
|  |  | ||||||
|  | class ZHLT01Climate : public climate_ir::ClimateIR { | ||||||
|  |  public: | ||||||
|  |   ZHLT01Climate() | ||||||
|  |       : climate_ir::ClimateIR( | ||||||
|  |             AC1_TEMP_MIN, AC1_TEMP_MAX, AC1_TEMP_INC, 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}, | ||||||
|  |             {climate::CLIMATE_PRESET_NONE, climate::CLIMATE_PRESET_SLEEP, climate::CLIMATE_PRESET_BOOST}) {} | ||||||
|  |  | ||||||
|  |   void setup() override { climate_ir::ClimateIR::setup(); } | ||||||
|  |  | ||||||
|  |  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; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace zhlt01 | ||||||
|  | }  // namespace esphome | ||||||
| @@ -2297,6 +2297,8 @@ climate: | |||||||
|     heat_mode: extended |     heat_mode: extended | ||||||
|   - platform: whynter |   - platform: whynter | ||||||
|     name: Whynter |     name: Whynter | ||||||
|  |   - platform: zhlt01 | ||||||
|  |     name: ZH/LT-01 Climate | ||||||
|  |  | ||||||
| script: | script: | ||||||
|   - id: climate_custom |   - id: climate_custom | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user