ESPHome 2026.10.0-dev
Loading...
Searching...
No Matches
climate_ir_lg.cpp
Go to the documentation of this file.
1#include "climate_ir_lg.h"
2#include "esphome/core/log.h"
3
5
6static const char *const TAG = "climate.climate_ir_lg";
7
8// All codes provided here are missing the checksum (last 4 bits)
9// this checksum needs to be calculated before sending (look at `calc_checksum_()`)
10
11const uint32_t LG_HEADER = 0x8800000;
12
13// Commands
17
19 HEADER_BASIC = 0x10000,
21
22 // JET MODE (only for cooling/drying/heating modes)
23 // For 30 minutes: max airflow (stronger than F5 aka FAN_MAX) + PO (min/min/max temperature respectively)
24 // After 30 minutes: F5 aka FAN_MAX + min/min/max temperature respectively
25 BASIC_JET = 0x080,
26};
27
29 HEADER_SYS = 0xC0000,
30
31 COMMAND_OFF = 0x050,
32
33 // Also known as 'auto-dry'
36
37 PURIFY_ON = 0x000, // From either OFF or Mode -> Purify
38 PURIFY_OFF = 0x080, // From Mode + Purify -> Mode
39
42
43 // ENERGY CTRL (only in Cooling mode)
44 COOL_ENERG_CTRL_80 = 0x7D0, // 80%
45 COOL_ENERG_CTRL_60 = 0x7E0, // 60%
46 COOL_ENERG_CTRL_40 = 0x800, // 40%
47 COOL_ENERG_CTRL_OFF = 0x7F0, // OFF
48
49 DISPLAY_KW = 0x460,
50 LIGHT_ON_OFF = 0x0A0,
51
52 TEMP_UNIT_F = 0x170,
53 TEMP_UNIT_C = 0x160,
54};
55
58
59 // Only 5 bits are relevant, I got 0x13952 once - not sure what is the 8th bit so ignoring that.
61
62 // Commands for Advanced Vertical Control: Swing + 6 fixed positions
63 VERT_FIX_1 = 0x040, // Down
64 VERT_FIX_2 = 0x050,
65 VERT_FIX_3 = 0x060,
66 VERT_FIX_4 = 0x070,
67 VERT_FIX_5 = 0x080,
68 VERT_FIX_6 = 0x090, // Up
69 VERT_SWING_ON = 0x140, // Swing between 1 and 6
70 VERT_SWING_OFF = 0x150, // Stops immediately
71
72 // Commands for Advanced Horizontal Control: Swing (3 modes) + 5 fixed positions
73 HORI_FIX_1 = 0x0B0, // Left
74 HORI_FIX_2 = 0x0C0,
75 HORI_FIX_3 = 0x0D0,
76 HORI_FIX_4 = 0x0E0,
77 HORI_FIX_5 = 0x0F0, // Right
78 HORI_SWING_ON_LEFT = 0x100, // Swing between 1 and 3
79 HORI_SWING_ON_RIGHT = 0x110, // Swing between 3 and 5
80 HORI_SWING_ON_FULL = 0x160, // Swing between 1 and 5
81 HORI_SWING_OFF = 0x170, // Stops immediately
82};
83
84// Following commands contain mode, fan speed and temperature
85
86// Modes
87const uint32_t COMMAND_ON_COOL = 0x00000;
88const uint32_t COMMAND_ON_DRY = 0x01000;
90const uint32_t COMMAND_ON_AI = 0x03000;
91const uint32_t COMMAND_ON_HEAT = 0x04000;
92
93const uint32_t COMMAND_COOL = 0x08000;
94const uint32_t COMMAND_DRY = 0x09000;
95const uint32_t COMMAND_FAN_ONLY = 0x0A000;
96const uint32_t COMMAND_AI = 0x0B000;
97const uint32_t COMMAND_HEAT = 0x0C000;
98
99// Fan speed
101const uint32_t FAN_AUTO = 0x50;
102const uint32_t FAN_MIN = 0x00; // AKA F1
103const uint32_t FAN_F2 = 0x90;
104const uint32_t FAN_MED = 0x20; // AKA F3
105const uint32_t FAN_F4 = 0xA0;
106const uint32_t FAN_MAX = 0x40; // AKA F5
107
108// Temperature
109const uint8_t TEMP_RANGE = TEMP_MAX - TEMP_MIN + 1;
110const uint32_t TEMP_MASK = 0xF00;
112
113const uint16_t BITS = 28;
114
116 uint32_t remote_state = LG_HEADER;
117
118 // ESP_LOGD(TAG, "climate_lg_ir mode_before_ code: 0x%02X", this->modeBefore_);
119
120 // Set command
121 if (this->send_swing_cmd_) {
122 this->send_swing_cmd_ = false;
123 if (this->advanced_commands_support_) {
124 switch (this->swing_mode) {
126 ESP_LOGD(TAG, "setting swing vertical");
127 remote_state |= CommandAdvSwing::HEADER_ADV_SWING;
128 remote_state |= CommandAdvSwing::VERT_SWING_ON;
129 break;
131 ESP_LOGD(TAG, "setting swing off");
132 remote_state |= CommandAdvSwing::HEADER_ADV_SWING;
133 remote_state |= CommandAdvSwing::VERT_SWING_OFF;
134 break;
135 default:
136 return;
137 }
138 this->transmit_(remote_state);
139 this->publish_state();
140 return;
141 } else { // just toggle swing when advanced_commands_support is not set
142 remote_state |= HEADER_BASIC;
143 remote_state |= BASIC_SWING_TOGGLE;
144 }
145 } else { // Mode commands
146 const bool climate_is_off = (this->mode_before_ == climate::CLIMATE_MODE_OFF);
147 switch (this->mode) {
149 remote_state |= climate_is_off ? COMMAND_ON_COOL : COMMAND_COOL;
150 break;
152 remote_state |= climate_is_off ? COMMAND_ON_DRY : COMMAND_DRY;
153 break;
155 remote_state |= climate_is_off ? COMMAND_ON_FAN_ONLY : COMMAND_FAN_ONLY;
156 break;
158 remote_state |= climate_is_off ? COMMAND_ON_AI : COMMAND_AI;
159 break;
161 remote_state |= climate_is_off ? COMMAND_ON_HEAT : COMMAND_HEAT;
162 break;
164 default:
165 remote_state |= CommandSys::HEADER_SYS;
166 remote_state |= CommandSys::COMMAND_OFF;
167 }
168 }
169
170 this->mode_before_ = this->mode;
171
172 ESP_LOGD(TAG, "climate_lg_ir mode code: 0x%02X", this->mode);
173
174 // Set fan speed
175 if (this->mode !=
176 climate::CLIMATE_MODE_OFF) { // https://github.com/esphome/esphome/pull/10875#issuecomment-5042765948
177 switch (this->fan_mode.value_or(climate::CLIMATE_FAN_ON)) {
179 remote_state |= FAN_MAX;
180 break;
182 remote_state |= FAN_MED;
183 break;
185 remote_state |= FAN_MIN;
186 break;
188 default:
189 remote_state |= FAN_AUTO;
190 break;
191 }
192 }
193
194 uint8_t temp;
195 switch (this->mode) {
197 if (!this->advanced_commands_support_) { // Keep previous behavior
198 break;
199 }
200 [[fallthrough]];
203 temp = static_cast<uint8_t>(roundf(clamp<float>(this->target_temperature, TEMP_MIN, TEMP_MAX)));
204 remote_state |= (temp - 15) << TEMP_SHIFT;
205 break;
206 default:
207 break;
208 }
209
210 this->transmit_(remote_state);
211 this->publish_state();
212}
213
215 uint8_t nbits = 0;
216 uint32_t remote_state = 0;
217
218 if (!data.expect_item(this->header_high_, this->header_low_))
219 return false;
220
221 for (nbits = 0; nbits < 32; nbits++) {
222 if (data.expect_item(this->bit_high_, this->bit_one_low_)) {
223 remote_state = (remote_state << 1) | 1;
224 } else if (data.expect_item(this->bit_high_, this->bit_zero_low_)) {
225 remote_state = (remote_state << 1) | 0;
226 } else if (nbits == BITS) {
227 break;
228 } else {
229 return false;
230 }
231 }
232
233 ESP_LOGD(TAG, "Received 0x%02" PRIX32, remote_state);
234 if ((remote_state & 0xFF00000) != LG_HEADER)
235 return false;
236
237 // Decode commands
238 switch (remote_state & COMMAND_HEADER_MASK) {
240 ESP_LOGD(TAG, "Got system command! With data: 0x%02" PRIX32, remote_state & COMMAND_DATA_MASK);
241 if ((remote_state & COMMAND_DATA_MASK) == CommandSys::COMMAND_OFF) {
243 } else {
244 return false;
245 }
246 break;
248 ESP_LOGD(TAG, "Got advanced swing command! With data: 0x%02" PRIX32,
250 switch (remote_state & CommandAdvSwing::ADV_SWING_DATA_MASK) {
253 break;
262 break;
263 default:
264 return false; // Ignore all other (horizontal) swing commands
265 }
266
267 this->publish_state();
268 return true;
269
270 case HEADER_BASIC:
271 if ((remote_state & COMMAND_DATA_MASK) == BASIC_JET) {
272 switch (this->mode) {
276 this->target_temperature =
279 // When enabling PO(WER) also known as JET mode, swing is set to VERT_3, but after 30 mins it will switch
280 // back to what it was before, so let's just not change it here it at all
281 this->publish_state();
282 return true;
283 default:
284 ESP_LOGD(TAG, "Got jet command, but current mode does not support it! Ignoring.");
285 return false;
286 }
287 }
288
289 // Keep previous behavior in case of other BASIC command
290 if (this->swing_mode == climate::CLIMATE_SWING_OFF) { // Just flip between vertical and off
292 } else {
294 }
295 this->publish_state();
296 return true;
297 // Following commands also contain fan speed and temperature, so no 'return' in these cases
298 case COMMAND_DRY:
299 case COMMAND_ON_DRY:
301 break;
302 case COMMAND_FAN_ONLY:
305 break;
306 case COMMAND_AI:
307 case COMMAND_ON_AI:
309 break;
310 case COMMAND_HEAT:
311 case COMMAND_ON_HEAT:
313 break;
314 case COMMAND_COOL:
315 case COMMAND_ON_COOL:
317 break;
318 default:
319 ESP_LOGD(TAG, "Got unknown command! Ignoring!");
320 return false;
321 }
322
323 // Decode fan speed
324 switch (remote_state & FAN_SPEED_MASK) {
325 case FAN_AUTO:
327 break;
328 case FAN_MIN:
329 case FAN_F2:
331 break;
332 case FAN_MED:
333 case FAN_F4:
335 break;
336 case FAN_MAX:
338 break;
339 default:
340 ESP_LOGD(TAG, "Got unknown fan speed! Ignoring!");
341 return false;
342 }
343
344 // Keep previous behavior
347 }
348
349 // Decode temperature for modes that support it
350 switch (this->mode) {
354 this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15;
355 break;
356 default:
357 break;
358 }
359
360 this->mode_before_ = this->mode;
361 this->publish_state();
362
363 return true;
364}
365
367 this->calc_checksum_(value);
368 ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02" PRIX32, value);
369
370 auto transmit = this->transmitter_->transmit();
371 auto *data = transmit.get_data();
372
373 data->set_carrier_frequency(38000);
374 data->reserve(2 + BITS * 2u);
375
376 data->item(this->header_high_, this->header_low_);
377
378 for (uint32_t mask = 1UL << (BITS - 1); mask != 0; mask >>= 1) {
379 if (value & mask) {
380 data->item(this->bit_high_, this->bit_one_low_);
381 } else {
382 data->item(this->bit_high_, this->bit_zero_low_);
383 }
384 }
385 data->mark(this->bit_high_);
386 transmit.perform();
387}
388
390 uint32_t sum = 0;
391 for (uint8_t i = 1; i < 8; i++) {
392 sum += (value & (CHECKSUM_MASK << (i * 4))) >> (i * 4);
393 }
394
395 value |= (sum & CHECKSUM_MASK);
396}
397
398} // namespace esphome::climate_ir_lg
ClimateMode mode
The active mode of the climate device.
Definition climate.h:304
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:298
float target_temperature
The target temperature of the climate device.
Definition climate.h:285
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:310
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:437
bool on_receive(remote_base::RemoteReceiveData data) override
Handle received IR Buffer.
void transmit_state() override
Transmit via IR the state of this climate controller.
const uint32_t COMMAND_COOL
const uint32_t CHECKSUM_MASK
const uint32_t COMMAND_DATA_MASK
const uint32_t COMMAND_ON_FAN_ONLY
const uint32_t COMMAND_HEADER_MASK
const uint32_t COMMAND_ON_DRY
const uint32_t FAN_SPEED_MASK
const uint32_t COMMAND_FAN_ONLY
const uint32_t COMMAND_ON_COOL
const uint32_t COMMAND_AI
const uint32_t COMMAND_DRY
const uint32_t COMMAND_HEAT
const uint32_t COMMAND_ON_AI
const uint32_t COMMAND_ON_HEAT
@ CLIMATE_SWING_OFF
The swing mode is set to Off.
@ CLIMATE_SWING_VERTICAL
The fan mode is set to Vertical.
@ CLIMATE_MODE_DRY
The climate device is set to dry/humidity mode.
@ CLIMATE_MODE_FAN_ONLY
The climate device only has the fan enabled, no heating or cooling is taking place.
@ CLIMATE_MODE_HEAT
The climate device is set to heat to reach the target temperature.
@ CLIMATE_MODE_COOL
The climate device is set to cool to reach the target temperature.
@ CLIMATE_MODE_HEAT_COOL
The climate device is set to heat/cool to reach the target temperature.
@ CLIMATE_MODE_OFF
The climate device is off.
@ CLIMATE_FAN_MEDIUM
The fan mode is set to Medium.
@ CLIMATE_FAN_ON
The fan mode is set to On.
@ CLIMATE_FAN_AUTO
The fan mode is set to Auto.
@ CLIMATE_FAN_LOW
The fan mode is set to Low.
@ CLIMATE_FAN_HIGH
The fan mode is set to High.
static void uint32_t