ESPHome 2025.5.0
Loading...
Searching...
No Matches
mitsubishi.cpp
Go to the documentation of this file.
1#include "mitsubishi.h"
2#include "esphome/core/log.h"
3
4namespace esphome {
5namespace mitsubishi {
6
7static const char *const TAG = "mitsubishi.climate";
8
9const uint8_t MITSUBISHI_OFF = 0x00;
10
11const uint8_t MITSUBISHI_MODE_AUTO = 0x20;
12const uint8_t MITSUBISHI_MODE_COOL = 0x18;
13const uint8_t MITSUBISHI_MODE_DRY = 0x10;
14const uint8_t MITSUBISHI_MODE_FAN_ONLY = 0x38;
15const uint8_t MITSUBISHI_MODE_HEAT = 0x08;
16
17const uint8_t MITSUBISHI_MODE_A_HEAT = 0x00;
18const uint8_t MITSUBISHI_MODE_A_DRY = 0x02;
19const uint8_t MITSUBISHI_MODE_A_COOL = 0x06;
20const uint8_t MITSUBISHI_MODE_A_AUTO = 0x06;
21
22const uint8_t MITSUBISHI_WIDE_VANE_SWING = 0xC0;
23
24const uint8_t MITSUBISHI_FAN_AUTO = 0x00;
25
26const uint8_t MITSUBISHI_VERTICAL_VANE_SWING = 0x38;
27
28// const uint8_t MITSUBISHI_AUTO = 0x80;
29const uint8_t MITSUBISHI_OTHERWISE = 0x40;
30const uint8_t MITSUBISHI_POWERFUL = 0x08;
31
32// Optional presets used to enable some model features
33const uint8_t MITSUBISHI_ECONOCOOL = 0x20;
34const uint8_t MITSUBISHI_NIGHTMODE = 0xC1;
35
36// Pulse parameters in usec
37const uint16_t MITSUBISHI_BIT_MARK = 430;
38const uint16_t MITSUBISHI_ONE_SPACE = 1250;
39const uint16_t MITSUBISHI_ZERO_SPACE = 390;
40const uint16_t MITSUBISHI_HEADER_MARK = 3500;
41const uint16_t MITSUBISHI_HEADER_SPACE = 1700;
42const uint16_t MITSUBISHI_MIN_GAP = 17500;
43
44// Marker bytes
45const uint8_t MITSUBISHI_BYTE00 = 0x23;
46const uint8_t MITSUBISHI_BYTE01 = 0xCB;
47const uint8_t MITSUBISHI_BYTE02 = 0x26;
48const uint8_t MITSUBISHI_BYTE03 = 0x01;
49const uint8_t MITSUBISHI_BYTE04 = 0x00;
50const uint8_t MITSUBISHI_BYTE13 = 0x00;
51const uint8_t MITSUBISHI_BYTE16 = 0x00;
52
61
62 if (this->supports_cool_)
64 if (this->supports_heat_)
66
67 if (this->supports_cool_ && this->supports_heat_)
69
70 if (this->supports_dry_)
72 if (this->supports_fan_only_)
74
75 // Default to only 3 levels in ESPHome even if most unit supports 4. The 3rd level is not used.
78 if (this->fan_mode_ == MITSUBISHI_FAN_Q4L)
80 if (/*this->fan_mode_ == MITSUBISHI_FAN_5L ||*/ this->fan_mode_ >= MITSUBISHI_FAN_4L)
81 traits.add_supported_fan_mode(climate::CLIMATE_FAN_MIDDLE); // Shouldn't be used for this but it helps
82
85
88
89 return traits;
90}
91
93 // Byte 0-4: Constant: 0x23, 0xCB, 0x26, 0x01, 0x00
94 // Byte 5: On=0x20, Off: 0x00
95 // Byte 6: MODE (See MODEs above (Heat/Dry/Cool/Auto/FanOnly)
96 // Byte 7: TEMP bits 0,1,2,3, added to MITSUBISHI_TEMP_MIN
97 // Example: 0x00 = 0°C+MITSUBISHI_TEMP_MIN = 16°C; 0x07 = 7°C+MITSUBISHI_TEMP_MIN = 23°C
98 // Byte 8: MODE_A & Wide Vane (if present)
99 // MODE_A bits 0,1,2 different than Byte 6 (See MODE_As above)
100 // Wide Vane bits 4,5,6,7 (Middle = 0x30)
101 // Byte 9: FAN/Vertical Vane/Switch To Auto
102 // FAN (Speed) bits 0,1,2
103 // Vertical Vane bits 3,4,5 (Auto = 0x00)
104 // Switch To Auto bits 6,7
105 // Byte 10: CLOCK Current time as configured on remote (0x00=Not used)
106 // Byte 11: END CLOCK Stop time of HVAC (0x00 for no setting)
107 // Byte 12: START CLOCK Start time of HVAC (0x00 for no setting)
108 // Byte 13: Constant 0x00
109 // Byte 14: HVAC specfic, i.e. ECONO COOL, CLEAN MODE, always 0x00
110 // Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00
111 // Byte 16: Constant 0x00
112 // Byte 17: Checksum: SUM[Byte0...Byte16]
113 uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
115
116 switch (this->mode) {
118 remote_state[6] = MITSUBISHI_MODE_HEAT;
119 remote_state[8] = MITSUBISHI_MODE_A_HEAT;
120 break;
122 remote_state[6] = MITSUBISHI_MODE_DRY;
123 remote_state[8] = MITSUBISHI_MODE_A_DRY;
124 break;
126 remote_state[6] = MITSUBISHI_MODE_COOL;
127 remote_state[8] = MITSUBISHI_MODE_A_COOL;
128 break;
130 remote_state[6] = MITSUBISHI_MODE_AUTO;
131 remote_state[8] = MITSUBISHI_MODE_A_AUTO;
132 break;
134 remote_state[6] = MITSUBISHI_MODE_FAN_ONLY;
135 remote_state[8] = MITSUBISHI_MODE_A_AUTO;
136 break;
138 default:
139 remote_state[6] = MITSUBISHI_MODE_COOL;
140 remote_state[8] = MITSUBISHI_MODE_A_COOL;
141 if (this->supports_heat_) {
142 remote_state[6] = MITSUBISHI_MODE_HEAT;
143 remote_state[8] = MITSUBISHI_MODE_A_HEAT;
144 }
145 remote_state[5] = MITSUBISHI_OFF;
146 break;
147 }
148
149 // Temperature
150 if (this->mode == climate::CLIMATE_MODE_DRY) {
151 remote_state[7] = 24 - MITSUBISHI_TEMP_MIN; // Remote sends always 24°C if "Dry" mode is selected
152 } else {
153 remote_state[7] = (uint8_t) roundf(
155 }
156
157 // Wide Vane
158 switch (this->swing_mode) {
161 remote_state[8] = remote_state[8] | MITSUBISHI_WIDE_VANE_SWING; // Wide Vane Swing
162 break;
164 default:
165 remote_state[8] = remote_state[8] | this->default_horizontal_direction_; // Off--> horizontal default position
166 break;
167 }
168
169 ESP_LOGD(TAG, "default_horizontal_direction_: %02X", this->default_horizontal_direction_);
170
171 // Fan Speed & Vertical Vane
172 // Map of Climate fan mode to this device expected value
173 // For 3Level: Low = 1, Medium = 2, High = 3
174 // For 4Level: Low = 1, Middle = 2, Medium = 3, High = 4
175 // For 5Level: Low = 1, Middle = 2, Medium = 3, High = 4
176 // For 4Level + Quiet: Low = 1, Middle = 2, Medium = 3, High = 4, Quiet = 5
177
178 switch (this->fan_mode.value()) {
180 remote_state[9] = 1;
181 break;
183 if (this->fan_mode_ == MITSUBISHI_FAN_3L) {
184 remote_state[9] = 2;
185 } else {
186 remote_state[9] = 3;
187 }
188 break;
190 if (this->fan_mode_ == MITSUBISHI_FAN_3L) {
191 remote_state[9] = 3;
192 } else {
193 remote_state[9] = 4;
194 }
195 break;
197 remote_state[9] = 2;
198 break;
200 remote_state[9] = 5;
201 break;
202 default:
203 remote_state[9] = MITSUBISHI_FAN_AUTO;
204 break;
205 }
206
207 ESP_LOGD(TAG, "fan: %02x state: %02x", this->fan_mode.value(), remote_state[9]);
208
209 // Vertical Vane
210 switch (this->swing_mode) {
213 remote_state[9] = remote_state[9] | MITSUBISHI_VERTICAL_VANE_SWING | MITSUBISHI_OTHERWISE; // Vane Swing
214 break;
216 default:
217 remote_state[9] = remote_state[9] | this->default_vertical_direction_ |
218 MITSUBISHI_OTHERWISE; // Off--> vertical default position
219 break;
220 }
221
222 ESP_LOGD(TAG, "default_vertical_direction_: %02X", this->default_vertical_direction_);
223
224 // Special modes
225 switch (this->preset.value()) {
227 remote_state[6] = MITSUBISHI_MODE_COOL | MITSUBISHI_OTHERWISE;
228 remote_state[8] = (remote_state[8] & ~7) | MITSUBISHI_MODE_A_COOL;
229 remote_state[14] = MITSUBISHI_ECONOCOOL;
230 break;
232 remote_state[9] = MITSUBISHI_FAN_AUTO;
233 remote_state[14] = MITSUBISHI_NIGHTMODE;
234 break;
236 remote_state[6] |= MITSUBISHI_OTHERWISE;
237 remote_state[15] = MITSUBISHI_POWERFUL;
238 break;
240 default:
241 break;
242 }
243
244 // Checksum
245 for (int i = 0; i < 17; i++) {
246 remote_state[17] += remote_state[i];
247 }
248
249 ESP_LOGD(TAG, "sending: %02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X",
250 remote_state[0], remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5],
251 remote_state[6], remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11],
252 remote_state[12], remote_state[13], remote_state[14], remote_state[15], remote_state[16], remote_state[17]);
253
254 auto transmit = this->transmitter_->transmit();
255 auto *data = transmit.get_data();
256
257 data->set_carrier_frequency(38000);
258 // repeat twice
259 for (uint8_t r = 0; r < 2; r++) {
260 // Header
261 data->mark(MITSUBISHI_HEADER_MARK);
262 data->space(MITSUBISHI_HEADER_SPACE);
263 // Data
264 for (uint8_t i : remote_state) {
265 for (uint8_t j = 0; j < 8; j++) {
266 data->mark(MITSUBISHI_BIT_MARK);
267 bool bit = i & (1 << j);
268 data->space(bit ? MITSUBISHI_ONE_SPACE : MITSUBISHI_ZERO_SPACE);
269 }
270 }
271 // Footer
272 if (r == 0) {
273 data->mark(MITSUBISHI_BIT_MARK);
274 data->space(MITSUBISHI_MIN_GAP); // Pause before repeating
275 }
276 }
277 data->mark(MITSUBISHI_BIT_MARK);
278
279 transmit.perform();
280}
281
282bool MitsubishiClimate::parse_state_frame_(const uint8_t frame[]) { return false; }
283
285 uint8_t state_frame[18] = {};
286
288 ESP_LOGV(TAG, "Header fail");
289 return false;
290 }
291
292 for (uint8_t pos = 0; pos < 18; pos++) {
293 uint8_t byte = 0;
294 for (int8_t bit = 0; bit < 8; bit++) {
296 byte |= 1 << bit;
298 ESP_LOGV(TAG, "Byte %d bit %d fail", pos, bit);
299 return false;
300 }
301 }
302 state_frame[pos] = byte;
303
304 // Check Header && Footer
305 if ((pos == 0 && byte != MITSUBISHI_BYTE00) || (pos == 1 && byte != MITSUBISHI_BYTE01) ||
306 (pos == 2 && byte != MITSUBISHI_BYTE02) || (pos == 3 && byte != MITSUBISHI_BYTE03) ||
307 (pos == 4 && byte != MITSUBISHI_BYTE04) || (pos == 13 && byte != MITSUBISHI_BYTE13) ||
308 (pos == 16 && byte != MITSUBISHI_BYTE16)) {
309 ESP_LOGV(TAG, "Bytes 0,1,2,3,4,13 or 16 fail - invalid value");
310 return false;
311 }
312 }
313
314 // On/Off and Mode
315 if (state_frame[5] == MITSUBISHI_OFF) {
317 } else {
318 switch (state_frame[6]) {
321 break;
324 break;
327 break;
330 break;
333 break;
334 }
335 }
336
337 // Temp
338 this->target_temperature = state_frame[7] + MITSUBISHI_TEMP_MIN;
339
340 // Fan
341 uint8_t fan = state_frame[9] & 0x07; //(Bit 0,1,2 = Speed)
342 // Map of Climate fan mode to this device expected value
343 // For 3Level: Low = 1, Medium = 2, High = 3
344 // For 4Level: Low = 1, Middle = 2, Medium = 3, High = 4
345 // For 5Level: Low = 1, Middle = 2, Medium = 3, High = 4
346 // For 4Level + Quiet: Low = 1, Middle = 2, Medium = 3, High = 4, Quiet = 5
347 climate::ClimateFanMode modes_mapping[8] = {
356 this->fan_mode = modes_mapping[fan];
357
358 // Wide Vane
359 uint8_t wide_vane = state_frame[8] & 0xF0; // Bits 4,5,6,7
360 switch (wide_vane) {
363 break;
364 default:
366 break;
367 }
368
369 // Vertical Vane
370 uint8_t vertical_vane = state_frame[9] & 0x38; // Bits 3,4,5
371 switch (vertical_vane) {
375 } else {
377 }
378 break;
379 }
380
381 switch (state_frame[14]) {
384 break;
387 break;
388 }
389
390 ESP_LOGV(TAG, "Receiving: %s", format_hex_pretty(state_frame, 18).c_str());
391
392 this->publish_state();
393 return true;
394}
395
396} // namespace mitsubishi
397} // namespace esphome
ClimateMode mode
The active mode of the climate device.
Definition climate.h:173
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:199
float target_temperature
The target temperature of the climate device.
Definition climate.h:186
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:202
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:395
optional< ClimatePreset > preset
The active preset of the climate device.
Definition climate.h:208
This class contains all static data for climate devices.
void set_visual_max_temperature(float visual_max_temperature)
void set_supported_modes(std::set< ClimateMode > modes)
void add_supported_fan_mode(ClimateFanMode mode)
void set_supports_action(bool supports_action)
void set_visual_temperature_step(float temperature_step)
void set_visual_min_temperature(float visual_min_temperature)
void set_supported_swing_modes(std::set< ClimateSwingMode > modes)
void set_supported_presets(std::set< ClimatePreset > presets)
void add_supported_mode(ClimateMode mode)
void set_supported_fan_modes(std::set< ClimateFanMode > modes)
void set_supports_current_temperature(bool supports_current_temperature)
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:89
bool expect_item(uint32_t mark, uint32_t space)
void set_carrier_frequency(uint32_t carrier_frequency)
Definition remote_base.h:34
@ 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.
@ 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
Definition mitsubishi.cpp:9
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
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
Definition helpers.cpp:372