ESPHome 2026.1.0-dev
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()) {
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", this->fan_mode.value(), remote_state[9]);
213
214 // Vertical Vane
215 switch (this->swing_mode) {
218 remote_state[9] = remote_state[9] | MITSUBISHI_VERTICAL_VANE_SWING | MITSUBISHI_OTHERWISE; // Vane Swing
219 break;
221 default:
222 remote_state[9] = remote_state[9] | this->default_vertical_direction_ |
223 MITSUBISHI_OTHERWISE; // Off--> vertical default position
224 break;
225 }
226
227 ESP_LOGD(TAG, "default_vertical_direction_: %02X", this->default_vertical_direction_);
228
229 // Special modes
230 switch (this->preset.value()) {
232 remote_state[6] = MITSUBISHI_MODE_COOL | MITSUBISHI_OTHERWISE;
233 remote_state[8] = (remote_state[8] & ~7) | MITSUBISHI_MODE_A_COOL;
234 remote_state[14] = MITSUBISHI_ECONOCOOL;
235 break;
237 remote_state[9] = MITSUBISHI_FAN_AUTO;
238 remote_state[14] = MITSUBISHI_NIGHTMODE;
239 break;
241 remote_state[6] |= MITSUBISHI_OTHERWISE;
242 remote_state[15] = MITSUBISHI_POWERFUL;
243 break;
245 default:
246 break;
247 }
248
249 // Checksum
250 for (int i = 0; i < 17; i++) {
251 remote_state[17] += remote_state[i];
252 }
253
254 ESP_LOGD(TAG, "sending: %02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X",
255 remote_state[0], remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5],
256 remote_state[6], remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11],
257 remote_state[12], remote_state[13], remote_state[14], remote_state[15], remote_state[16], remote_state[17]);
258
259 auto transmit = this->transmitter_->transmit();
260 auto *data = transmit.get_data();
261
262 data->set_carrier_frequency(38000);
263 // repeat twice
264 for (uint8_t r = 0; r < 2; r++) {
265 // Header
266 data->mark(MITSUBISHI_HEADER_MARK);
267 data->space(MITSUBISHI_HEADER_SPACE);
268 // Data
269 for (uint8_t i : remote_state) {
270 for (uint8_t j = 0; j < 8; j++) {
271 data->mark(MITSUBISHI_BIT_MARK);
272 bool bit = i & (1 << j);
273 data->space(bit ? MITSUBISHI_ONE_SPACE : MITSUBISHI_ZERO_SPACE);
274 }
275 }
276 // Footer
277 if (r == 0) {
278 data->mark(MITSUBISHI_BIT_MARK);
279 data->space(MITSUBISHI_MIN_GAP); // Pause before repeating
280 }
281 }
282 data->mark(MITSUBISHI_BIT_MARK);
283
284 transmit.perform();
285}
286
287bool MitsubishiClimate::parse_state_frame_(const uint8_t frame[]) { return false; }
288
290 uint8_t state_frame[18] = {};
291
293 ESP_LOGV(TAG, "Header fail");
294 return false;
295 }
296
297 for (uint8_t pos = 0; pos < 18; pos++) {
298 uint8_t byte = 0;
299 for (int8_t bit = 0; bit < 8; bit++) {
301 byte |= 1 << bit;
303 ESP_LOGV(TAG, "Byte %d bit %d fail", pos, bit);
304 return false;
305 }
306 }
307 state_frame[pos] = byte;
308
309 // Check Header && Footer
310 if ((pos == 0 && byte != MITSUBISHI_BYTE00) || (pos == 1 && byte != MITSUBISHI_BYTE01) ||
311 (pos == 2 && byte != MITSUBISHI_BYTE02) || (pos == 3 && byte != MITSUBISHI_BYTE03) ||
312 (pos == 4 && byte != MITSUBISHI_BYTE04) || (pos == 13 && byte != MITSUBISHI_BYTE13) ||
313 (pos == 16 && byte != MITSUBISHI_BYTE16)) {
314 ESP_LOGV(TAG, "Bytes 0,1,2,3,4,13 or 16 fail - invalid value");
315 return false;
316 }
317 }
318
319 // On/Off and Mode
320 if (state_frame[5] == MITSUBISHI_OFF) {
322 } else {
323 switch (state_frame[6]) {
326 break;
329 break;
332 break;
335 break;
338 break;
339 }
340 }
341
342 // Temp
343 this->target_temperature = state_frame[7] + MITSUBISHI_TEMP_MIN;
344
345 // Fan
346 uint8_t fan = state_frame[9] & 0x07; //(Bit 0,1,2 = Speed)
347 // Map of Climate fan mode to this device expected value
348 // For 3Level: Low = 1, Medium = 2, High = 3
349 // For 4Level: Low = 1, Middle = 2, Medium = 3, High = 4
350 // For 5Level: Low = 1, Middle = 2, Medium = 3, High = 4
351 // For 4Level + Quiet: Low = 1, Middle = 2, Medium = 3, High = 4, Quiet = 5
352 climate::ClimateFanMode modes_mapping[8] = {
361 this->fan_mode = modes_mapping[fan];
362
363 // Wide Vane
364 uint8_t wide_vane = state_frame[8] & 0xF0; // Bits 4,5,6,7
365 switch (wide_vane) {
368 break;
369 default:
371 break;
372 }
373
374 // Vertical Vane
375 uint8_t vertical_vane = state_frame[9] & 0x38; // Bits 3,4,5
376 switch (vertical_vane) {
380 } else {
382 }
383 break;
384 }
385
386 switch (state_frame[14]) {
389 break;
392 break;
393 }
394
395#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
396 char hex_buf[format_hex_pretty_size(MITSUBISHI_FRAME_SIZE)];
397#endif
398 ESP_LOGV(TAG, "Receiving: %s", format_hex_pretty_to(hex_buf, state_frame, MITSUBISHI_FRAME_SIZE));
399
400 this->publish_state();
401 return true;
402}
403
404} // namespace mitsubishi
405} // namespace esphome
ClimateMode mode
The active mode of the climate device.
Definition climate.h:261
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:255
float target_temperature
The target temperature of the climate device.
Definition climate.h:242
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:267
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:438
optional< ClimatePreset > preset
The active preset of the climate device.
Definition climate.h:258
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[])
value_type const & value() const
Definition optional.h:94
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_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:331
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:735