ESPHome 2026.3.0
Loading...
Searching...
No Matches
mitsubishi.cpp
Go to the documentation of this file.
1#include "mitsubishi.h"
3#include "esphome/core/log.h"
4
5namespace esphome {
6namespace mitsubishi {
7
8static const char *const TAG = "mitsubishi.climate";
9
10// IR frame size for Mitsubishi climate
11static constexpr size_t MITSUBISHI_FRAME_SIZE = 18;
12
13const uint8_t MITSUBISHI_OFF = 0x00;
14
15const uint8_t MITSUBISHI_MODE_AUTO = 0x20;
16const uint8_t MITSUBISHI_MODE_COOL = 0x18;
17const uint8_t MITSUBISHI_MODE_DRY = 0x10;
18const uint8_t MITSUBISHI_MODE_FAN_ONLY = 0x38;
19const uint8_t MITSUBISHI_MODE_HEAT = 0x08;
20
21const uint8_t MITSUBISHI_MODE_A_HEAT = 0x00;
22const uint8_t MITSUBISHI_MODE_A_DRY = 0x02;
23const uint8_t MITSUBISHI_MODE_A_COOL = 0x06;
24const uint8_t MITSUBISHI_MODE_A_AUTO = 0x06;
25
26const uint8_t MITSUBISHI_WIDE_VANE_SWING = 0xC0;
27
28const uint8_t MITSUBISHI_FAN_AUTO = 0x00;
29
30const uint8_t MITSUBISHI_VERTICAL_VANE_SWING = 0x38;
31
32// const uint8_t MITSUBISHI_AUTO = 0x80;
33const uint8_t MITSUBISHI_OTHERWISE = 0x40;
34const uint8_t MITSUBISHI_POWERFUL = 0x08;
35
36// Optional presets used to enable some model features
37const uint8_t MITSUBISHI_ECONOCOOL = 0x20;
38const uint8_t MITSUBISHI_NIGHTMODE = 0xC1;
39
40// Pulse parameters in usec
41const uint16_t MITSUBISHI_BIT_MARK = 430;
42const uint16_t MITSUBISHI_ONE_SPACE = 1250;
43const uint16_t MITSUBISHI_ZERO_SPACE = 390;
44const uint16_t MITSUBISHI_HEADER_MARK = 3500;
45const uint16_t MITSUBISHI_HEADER_SPACE = 1700;
46const uint16_t MITSUBISHI_MIN_GAP = 17500;
47
48// Marker bytes
49const uint8_t MITSUBISHI_BYTE00 = 0x23;
50const uint8_t MITSUBISHI_BYTE01 = 0xCB;
51const uint8_t MITSUBISHI_BYTE02 = 0x26;
52const uint8_t MITSUBISHI_BYTE03 = 0x01;
53const uint8_t MITSUBISHI_BYTE04 = 0x00;
54const uint8_t MITSUBISHI_BYTE13 = 0x00;
55const uint8_t MITSUBISHI_BYTE16 = 0x00;
56
59 if (this->sensor_ != nullptr) {
61 }
66
67 if (this->supports_cool_)
69 if (this->supports_heat_)
71
72 if (this->supports_cool_ && this->supports_heat_)
74
75 if (this->supports_dry_)
77 if (this->supports_fan_only_)
79
80 // Default to only 3 levels in ESPHome even if most unit supports 4. The 3rd level is not used.
83 if (this->fan_mode_ == MITSUBISHI_FAN_Q4L)
85 if (/*this->fan_mode_ == MITSUBISHI_FAN_5L ||*/ this->fan_mode_ >= MITSUBISHI_FAN_4L)
86 traits.add_supported_fan_mode(climate::CLIMATE_FAN_MIDDLE); // Shouldn't be used for this but it helps
87
90
93
94 return traits;
95}
96
98 // Byte 0-4: Constant: 0x23, 0xCB, 0x26, 0x01, 0x00
99 // Byte 5: On=0x20, Off: 0x00
100 // Byte 6: MODE (See MODEs above (Heat/Dry/Cool/Auto/FanOnly)
101 // Byte 7: TEMP bits 0,1,2,3, added to MITSUBISHI_TEMP_MIN
102 // Example: 0x00 = 0°C+MITSUBISHI_TEMP_MIN = 16°C; 0x07 = 7°C+MITSUBISHI_TEMP_MIN = 23°C
103 // Byte 8: MODE_A & Wide Vane (if present)
104 // MODE_A bits 0,1,2 different than Byte 6 (See MODE_As above)
105 // Wide Vane bits 4,5,6,7 (Middle = 0x30)
106 // Byte 9: FAN/Vertical Vane/Switch To Auto
107 // FAN (Speed) bits 0,1,2
108 // Vertical Vane bits 3,4,5 (Auto = 0x00)
109 // Switch To Auto bits 6,7
110 // Byte 10: CLOCK Current time as configured on remote (0x00=Not used)
111 // Byte 11: END CLOCK Stop time of HVAC (0x00 for no setting)
112 // Byte 12: START CLOCK Start time of HVAC (0x00 for no setting)
113 // Byte 13: Constant 0x00
114 // Byte 14: HVAC specfic, i.e. ECONO COOL, CLEAN MODE, always 0x00
115 // Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00
116 // Byte 16: Constant 0x00
117 // Byte 17: Checksum: SUM[Byte0...Byte16]
118 uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
120
121 switch (this->mode) {
123 remote_state[6] = MITSUBISHI_MODE_HEAT;
124 remote_state[8] = MITSUBISHI_MODE_A_HEAT;
125 break;
127 remote_state[6] = MITSUBISHI_MODE_DRY;
128 remote_state[8] = MITSUBISHI_MODE_A_DRY;
129 break;
131 remote_state[6] = MITSUBISHI_MODE_COOL;
132 remote_state[8] = MITSUBISHI_MODE_A_COOL;
133 break;
135 remote_state[6] = MITSUBISHI_MODE_AUTO;
136 remote_state[8] = MITSUBISHI_MODE_A_AUTO;
137 break;
139 remote_state[6] = MITSUBISHI_MODE_FAN_ONLY;
140 remote_state[8] = MITSUBISHI_MODE_A_AUTO;
141 break;
143 default:
144 remote_state[6] = MITSUBISHI_MODE_COOL;
145 remote_state[8] = MITSUBISHI_MODE_A_COOL;
146 if (this->supports_heat_) {
147 remote_state[6] = MITSUBISHI_MODE_HEAT;
148 remote_state[8] = MITSUBISHI_MODE_A_HEAT;
149 }
150 remote_state[5] = MITSUBISHI_OFF;
151 break;
152 }
153
154 // Temperature
155 if (this->mode == climate::CLIMATE_MODE_DRY) {
156 remote_state[7] = 24 - MITSUBISHI_TEMP_MIN; // Remote sends always 24°C if "Dry" mode is selected
157 } else {
158 remote_state[7] = (uint8_t) roundf(
160 }
161
162 // Wide Vane
163 switch (this->swing_mode) {
166 remote_state[8] = remote_state[8] | MITSUBISHI_WIDE_VANE_SWING; // Wide Vane Swing
167 break;
169 default:
170 remote_state[8] = remote_state[8] | this->default_horizontal_direction_; // Off--> horizontal default position
171 break;
172 }
173
174 ESP_LOGD(TAG, "default_horizontal_direction_: %02X", this->default_horizontal_direction_);
175
176 // Fan Speed & Vertical Vane
177 // Map of Climate fan mode to this device expected value
178 // For 3Level: Low = 1, Medium = 2, High = 3
179 // For 4Level: Low = 1, Middle = 2, Medium = 3, High = 4
180 // For 5Level: Low = 1, Middle = 2, Medium = 3, High = 4
181 // For 4Level + Quiet: Low = 1, Middle = 2, Medium = 3, High = 4, Quiet = 5
182
183 switch (this->fan_mode.value_or(climate::CLIMATE_FAN_ON)) {
185 remote_state[9] = 1;
186 break;
188 if (this->fan_mode_ == MITSUBISHI_FAN_3L) {
189 remote_state[9] = 2;
190 } else {
191 remote_state[9] = 3;
192 }
193 break;
195 if (this->fan_mode_ == MITSUBISHI_FAN_3L) {
196 remote_state[9] = 3;
197 } else {
198 remote_state[9] = 4;
199 }
200 break;
202 remote_state[9] = 2;
203 break;
205 remote_state[9] = 5;
206 break;
207 default:
208 remote_state[9] = MITSUBISHI_FAN_AUTO;
209 break;
210 }
211
212 ESP_LOGD(TAG, "fan: %02x state: %02x", static_cast<uint8_t>(this->fan_mode.value_or(climate::CLIMATE_FAN_ON)),
213 remote_state[9]);
214
215 // Vertical Vane
216 switch (this->swing_mode) {
219 remote_state[9] = remote_state[9] | MITSUBISHI_VERTICAL_VANE_SWING | MITSUBISHI_OTHERWISE; // Vane Swing
220 break;
222 default:
223 remote_state[9] = remote_state[9] | this->default_vertical_direction_ |
224 MITSUBISHI_OTHERWISE; // Off--> vertical default position
225 break;
226 }
227
228 ESP_LOGD(TAG, "default_vertical_direction_: %02X", this->default_vertical_direction_);
229
230 // Special modes
231 switch (this->preset.value_or(climate::CLIMATE_PRESET_NONE)) {
233 remote_state[6] = MITSUBISHI_MODE_COOL | MITSUBISHI_OTHERWISE;
234 remote_state[8] = (remote_state[8] & ~7) | MITSUBISHI_MODE_A_COOL;
235 remote_state[14] = MITSUBISHI_ECONOCOOL;
236 break;
238 remote_state[9] = MITSUBISHI_FAN_AUTO;
239 remote_state[14] = MITSUBISHI_NIGHTMODE;
240 break;
242 remote_state[6] |= MITSUBISHI_OTHERWISE;
243 remote_state[15] = MITSUBISHI_POWERFUL;
244 break;
246 default:
247 break;
248 }
249
250 // Checksum
251 for (int i = 0; i < 17; i++) {
252 remote_state[17] += remote_state[i];
253 }
254
255 ESP_LOGD(TAG, "sending: %02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X",
256 remote_state[0], remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5],
257 remote_state[6], remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11],
258 remote_state[12], remote_state[13], remote_state[14], remote_state[15], remote_state[16], remote_state[17]);
259
260 auto transmit = this->transmitter_->transmit();
261 auto *data = transmit.get_data();
262
263 data->set_carrier_frequency(38000);
264 // repeat twice
265 for (uint8_t r = 0; r < 2; r++) {
266 // Header
267 data->mark(MITSUBISHI_HEADER_MARK);
268 data->space(MITSUBISHI_HEADER_SPACE);
269 // Data
270 for (uint8_t i : remote_state) {
271 for (uint8_t j = 0; j < 8; j++) {
272 data->mark(MITSUBISHI_BIT_MARK);
273 bool bit = i & (1 << j);
274 data->space(bit ? MITSUBISHI_ONE_SPACE : MITSUBISHI_ZERO_SPACE);
275 }
276 }
277 // Footer
278 if (r == 0) {
279 data->mark(MITSUBISHI_BIT_MARK);
280 data->space(MITSUBISHI_MIN_GAP); // Pause before repeating
281 }
282 }
283 data->mark(MITSUBISHI_BIT_MARK);
284
285 transmit.perform();
286}
287
288bool MitsubishiClimate::parse_state_frame_(const uint8_t frame[]) { return false; }
289
291 uint8_t state_frame[18] = {};
292
294 ESP_LOGV(TAG, "Header fail");
295 return false;
296 }
297
298 for (uint8_t pos = 0; pos < 18; pos++) {
299 uint8_t byte = 0;
300 for (int8_t bit = 0; bit < 8; bit++) {
302 byte |= 1 << bit;
304 ESP_LOGV(TAG, "Byte %d bit %d fail", pos, bit);
305 return false;
306 }
307 }
308 state_frame[pos] = byte;
309
310 // Check Header && Footer
311 if ((pos == 0 && byte != MITSUBISHI_BYTE00) || (pos == 1 && byte != MITSUBISHI_BYTE01) ||
312 (pos == 2 && byte != MITSUBISHI_BYTE02) || (pos == 3 && byte != MITSUBISHI_BYTE03) ||
313 (pos == 4 && byte != MITSUBISHI_BYTE04) || (pos == 13 && byte != MITSUBISHI_BYTE13) ||
314 (pos == 16 && byte != MITSUBISHI_BYTE16)) {
315 ESP_LOGV(TAG, "Bytes 0,1,2,3,4,13 or 16 fail - invalid value");
316 return false;
317 }
318 }
319
320 // On/Off and Mode
321 if (state_frame[5] == MITSUBISHI_OFF) {
323 } else {
324 switch (state_frame[6]) {
327 break;
330 break;
333 break;
336 break;
339 break;
340 }
341 }
342
343 // Temp
344 this->target_temperature = state_frame[7] + MITSUBISHI_TEMP_MIN;
345
346 // Fan
347 uint8_t fan = state_frame[9] & 0x07; //(Bit 0,1,2 = Speed)
348 // Map of Climate fan mode to this device expected value
349 // For 3Level: Low = 1, Medium = 2, High = 3
350 // For 4Level: Low = 1, Middle = 2, Medium = 3, High = 4
351 // For 5Level: Low = 1, Middle = 2, Medium = 3, High = 4
352 // For 4Level + Quiet: Low = 1, Middle = 2, Medium = 3, High = 4, Quiet = 5
353 climate::ClimateFanMode modes_mapping[8] = {
362 this->fan_mode = modes_mapping[fan];
363
364 // Wide Vane
365 uint8_t wide_vane = state_frame[8] & 0xF0; // Bits 4,5,6,7
366 switch (wide_vane) {
369 break;
370 default:
372 break;
373 }
374
375 // Vertical Vane
376 uint8_t vertical_vane = state_frame[9] & 0x38; // Bits 3,4,5
377 switch (vertical_vane) {
381 } else {
383 }
384 break;
385 }
386
387 switch (state_frame[14]) {
390 break;
393 break;
394 }
395
396#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
397 char hex_buf[format_hex_pretty_size(MITSUBISHI_FRAME_SIZE)];
398#endif
399 ESP_LOGV(TAG, "Receiving: %s", format_hex_pretty_to(hex_buf, state_frame, MITSUBISHI_FRAME_SIZE));
400
401 this->publish_state();
402 return true;
403}
404
405} // namespace mitsubishi
406} // namespace esphome
ClimateMode mode
The active mode of the climate device.
Definition climate.h:266
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:260
float target_temperature
The target temperature of the climate device.
Definition climate.h:247
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:272
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:445
optional< ClimatePreset > preset
The active preset of the climate device.
Definition climate.h:263
void set_visual_max_temperature(float visual_max_temperature)
void add_feature_flags(uint32_t feature_flags)
void add_supported_fan_mode(ClimateFanMode mode)
void set_visual_temperature_step(float temperature_step)
void set_supported_presets(ClimatePresetMask presets)
void set_supported_swing_modes(ClimateSwingModeMask modes)
void set_visual_min_temperature(float visual_min_temperature)
void set_supported_modes(ClimateModeMask modes)
void add_supported_mode(ClimateMode mode)
void set_supported_fan_modes(ClimateFanModeMask modes)
climate::ClimateTraits traits() override
bool on_receive(remote_base::RemoteReceiveData data) override
VerticalDirection default_vertical_direction_
Definition mitsubishi.h:77
HorizontalDirection default_horizontal_direction_
Definition mitsubishi.h:76
bool parse_state_frame_(const uint8_t frame[])
bool expect_item(uint32_t mark, uint32_t space)
void set_carrier_frequency(uint32_t carrier_frequency)
Definition remote_base.h:30
@ CLIMATE_SUPPORTS_CURRENT_TEMPERATURE
@ CLIMATE_PRESET_NONE
No preset is active.
@ CLIMATE_PRESET_BOOST
Device is in boost preset.
@ CLIMATE_PRESET_SLEEP
Device is prepared for sleep.
@ CLIMATE_PRESET_ECO
Device is running an energy-saving preset.
@ CLIMATE_SWING_OFF
The swing mode is set to Off.
@ CLIMATE_SWING_HORIZONTAL
The fan mode is set to Horizontal.
@ CLIMATE_SWING_VERTICAL
The fan mode is set to Vertical.
@ CLIMATE_SWING_BOTH
The fan mode is set to Both.
@ 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.
ClimateFanMode
NOTE: If adding values, update ClimateFanModeMask in climate_traits.h to use the new last value.
@ 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_MIDDLE
The fan mode is set to Middle.
@ CLIMATE_FAN_QUIET
The fan mode is set to Quiet.
@ CLIMATE_FAN_HIGH
The fan mode is set to High.
const uint8_t MITSUBISHI_POWERFUL
const uint16_t MITSUBISHI_ONE_SPACE
const uint16_t MITSUBISHI_MIN_GAP
const uint8_t MITSUBISHI_MODE_AUTO
const uint8_t MITSUBISHI_MODE_COOL
const uint16_t MITSUBISHI_ZERO_SPACE
const uint8_t MITSUBISHI_FAN_AUTO
const uint8_t MITSUBISHI_BYTE00
const uint16_t MITSUBISHI_HEADER_MARK
const uint8_t MITSUBISHI_BYTE13
const uint8_t MITSUBISHI_BYTE01
const uint8_t MITSUBISHI_MODE_A_HEAT
const uint8_t MITSUBISHI_BYTE04
const uint8_t MITSUBISHI_BYTE03
const uint8_t MITSUBISHI_BYTE02
const uint8_t MITSUBISHI_TEMP_MAX
Definition mitsubishi.h:12
const uint8_t MITSUBISHI_NIGHTMODE
const uint8_t MITSUBISHI_TEMP_MIN
Definition mitsubishi.h:11
const uint8_t MITSUBISHI_WIDE_VANE_SWING
const uint16_t MITSUBISHI_HEADER_SPACE
const uint8_t MITSUBISHI_VERTICAL_VANE_SWING
const uint8_t MITSUBISHI_MODE_A_DRY
const uint8_t MITSUBISHI_OTHERWISE
const uint8_t MITSUBISHI_ECONOCOOL
const uint8_t MITSUBISHI_OFF
const uint8_t MITSUBISHI_MODE_A_COOL
const uint8_t MITSUBISHI_MODE_DRY
const uint8_t MITSUBISHI_BYTE16
const uint8_t MITSUBISHI_MODE_FAN_ONLY
const uint8_t MITSUBISHI_MODE_A_AUTO
const uint16_t MITSUBISHI_BIT_MARK
const uint8_t MITSUBISHI_MODE_HEAT
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
Definition helpers.cpp:353
size_t size_t pos
Definition helpers.h:929
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
Definition helpers.h:1200