ESPHome 2025.12.0-dev
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
55 if (this->sensor_ != nullptr) {
57 }
62
63 if (this->supports_cool_)
65 if (this->supports_heat_)
67
68 if (this->supports_cool_ && this->supports_heat_)
70
71 if (this->supports_dry_)
73 if (this->supports_fan_only_)
75
76 // Default to only 3 levels in ESPHome even if most unit supports 4. The 3rd level is not used.
79 if (this->fan_mode_ == MITSUBISHI_FAN_Q4L)
81 if (/*this->fan_mode_ == MITSUBISHI_FAN_5L ||*/ this->fan_mode_ >= MITSUBISHI_FAN_4L)
82 traits.add_supported_fan_mode(climate::CLIMATE_FAN_MIDDLE); // Shouldn't be used for this but it helps
83
86
89
90 return traits;
91}
92
94 // Byte 0-4: Constant: 0x23, 0xCB, 0x26, 0x01, 0x00
95 // Byte 5: On=0x20, Off: 0x00
96 // Byte 6: MODE (See MODEs above (Heat/Dry/Cool/Auto/FanOnly)
97 // Byte 7: TEMP bits 0,1,2,3, added to MITSUBISHI_TEMP_MIN
98 // Example: 0x00 = 0°C+MITSUBISHI_TEMP_MIN = 16°C; 0x07 = 7°C+MITSUBISHI_TEMP_MIN = 23°C
99 // Byte 8: MODE_A & Wide Vane (if present)
100 // MODE_A bits 0,1,2 different than Byte 6 (See MODE_As above)
101 // Wide Vane bits 4,5,6,7 (Middle = 0x30)
102 // Byte 9: FAN/Vertical Vane/Switch To Auto
103 // FAN (Speed) bits 0,1,2
104 // Vertical Vane bits 3,4,5 (Auto = 0x00)
105 // Switch To Auto bits 6,7
106 // Byte 10: CLOCK Current time as configured on remote (0x00=Not used)
107 // Byte 11: END CLOCK Stop time of HVAC (0x00 for no setting)
108 // Byte 12: START CLOCK Start time of HVAC (0x00 for no setting)
109 // Byte 13: Constant 0x00
110 // Byte 14: HVAC specfic, i.e. ECONO COOL, CLEAN MODE, always 0x00
111 // Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00
112 // Byte 16: Constant 0x00
113 // Byte 17: Checksum: SUM[Byte0...Byte16]
114 uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
116
117 switch (this->mode) {
119 remote_state[6] = MITSUBISHI_MODE_HEAT;
120 remote_state[8] = MITSUBISHI_MODE_A_HEAT;
121 break;
123 remote_state[6] = MITSUBISHI_MODE_DRY;
124 remote_state[8] = MITSUBISHI_MODE_A_DRY;
125 break;
127 remote_state[6] = MITSUBISHI_MODE_COOL;
128 remote_state[8] = MITSUBISHI_MODE_A_COOL;
129 break;
131 remote_state[6] = MITSUBISHI_MODE_AUTO;
132 remote_state[8] = MITSUBISHI_MODE_A_AUTO;
133 break;
135 remote_state[6] = MITSUBISHI_MODE_FAN_ONLY;
136 remote_state[8] = MITSUBISHI_MODE_A_AUTO;
137 break;
139 default:
140 remote_state[6] = MITSUBISHI_MODE_COOL;
141 remote_state[8] = MITSUBISHI_MODE_A_COOL;
142 if (this->supports_heat_) {
143 remote_state[6] = MITSUBISHI_MODE_HEAT;
144 remote_state[8] = MITSUBISHI_MODE_A_HEAT;
145 }
146 remote_state[5] = MITSUBISHI_OFF;
147 break;
148 }
149
150 // Temperature
151 if (this->mode == climate::CLIMATE_MODE_DRY) {
152 remote_state[7] = 24 - MITSUBISHI_TEMP_MIN; // Remote sends always 24°C if "Dry" mode is selected
153 } else {
154 remote_state[7] = (uint8_t) roundf(
156 }
157
158 // Wide Vane
159 switch (this->swing_mode) {
162 remote_state[8] = remote_state[8] | MITSUBISHI_WIDE_VANE_SWING; // Wide Vane Swing
163 break;
165 default:
166 remote_state[8] = remote_state[8] | this->default_horizontal_direction_; // Off--> horizontal default position
167 break;
168 }
169
170 ESP_LOGD(TAG, "default_horizontal_direction_: %02X", this->default_horizontal_direction_);
171
172 // Fan Speed & Vertical Vane
173 // Map of Climate fan mode to this device expected value
174 // For 3Level: Low = 1, Medium = 2, High = 3
175 // For 4Level: Low = 1, Middle = 2, Medium = 3, High = 4
176 // For 5Level: Low = 1, Middle = 2, Medium = 3, High = 4
177 // For 4Level + Quiet: Low = 1, Middle = 2, Medium = 3, High = 4, Quiet = 5
178
179 switch (this->fan_mode.value()) {
181 remote_state[9] = 1;
182 break;
184 if (this->fan_mode_ == MITSUBISHI_FAN_3L) {
185 remote_state[9] = 2;
186 } else {
187 remote_state[9] = 3;
188 }
189 break;
191 if (this->fan_mode_ == MITSUBISHI_FAN_3L) {
192 remote_state[9] = 3;
193 } else {
194 remote_state[9] = 4;
195 }
196 break;
198 remote_state[9] = 2;
199 break;
201 remote_state[9] = 5;
202 break;
203 default:
204 remote_state[9] = MITSUBISHI_FAN_AUTO;
205 break;
206 }
207
208 ESP_LOGD(TAG, "fan: %02x state: %02x", this->fan_mode.value(), remote_state[9]);
209
210 // Vertical Vane
211 switch (this->swing_mode) {
214 remote_state[9] = remote_state[9] | MITSUBISHI_VERTICAL_VANE_SWING | MITSUBISHI_OTHERWISE; // Vane Swing
215 break;
217 default:
218 remote_state[9] = remote_state[9] | this->default_vertical_direction_ |
219 MITSUBISHI_OTHERWISE; // Off--> vertical default position
220 break;
221 }
222
223 ESP_LOGD(TAG, "default_vertical_direction_: %02X", this->default_vertical_direction_);
224
225 // Special modes
226 switch (this->preset.value()) {
228 remote_state[6] = MITSUBISHI_MODE_COOL | MITSUBISHI_OTHERWISE;
229 remote_state[8] = (remote_state[8] & ~7) | MITSUBISHI_MODE_A_COOL;
230 remote_state[14] = MITSUBISHI_ECONOCOOL;
231 break;
233 remote_state[9] = MITSUBISHI_FAN_AUTO;
234 remote_state[14] = MITSUBISHI_NIGHTMODE;
235 break;
237 remote_state[6] |= MITSUBISHI_OTHERWISE;
238 remote_state[15] = MITSUBISHI_POWERFUL;
239 break;
241 default:
242 break;
243 }
244
245 // Checksum
246 for (int i = 0; i < 17; i++) {
247 remote_state[17] += remote_state[i];
248 }
249
250 ESP_LOGD(TAG, "sending: %02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X",
251 remote_state[0], remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5],
252 remote_state[6], remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11],
253 remote_state[12], remote_state[13], remote_state[14], remote_state[15], remote_state[16], remote_state[17]);
254
255 auto transmit = this->transmitter_->transmit();
256 auto *data = transmit.get_data();
257
258 data->set_carrier_frequency(38000);
259 // repeat twice
260 for (uint8_t r = 0; r < 2; r++) {
261 // Header
262 data->mark(MITSUBISHI_HEADER_MARK);
263 data->space(MITSUBISHI_HEADER_SPACE);
264 // Data
265 for (uint8_t i : remote_state) {
266 for (uint8_t j = 0; j < 8; j++) {
267 data->mark(MITSUBISHI_BIT_MARK);
268 bool bit = i & (1 << j);
269 data->space(bit ? MITSUBISHI_ONE_SPACE : MITSUBISHI_ZERO_SPACE);
270 }
271 }
272 // Footer
273 if (r == 0) {
274 data->mark(MITSUBISHI_BIT_MARK);
275 data->space(MITSUBISHI_MIN_GAP); // Pause before repeating
276 }
277 }
278 data->mark(MITSUBISHI_BIT_MARK);
279
280 transmit.perform();
281}
282
283bool MitsubishiClimate::parse_state_frame_(const uint8_t frame[]) { return false; }
284
286 uint8_t state_frame[18] = {};
287
289 ESP_LOGV(TAG, "Header fail");
290 return false;
291 }
292
293 for (uint8_t pos = 0; pos < 18; pos++) {
294 uint8_t byte = 0;
295 for (int8_t bit = 0; bit < 8; bit++) {
297 byte |= 1 << bit;
299 ESP_LOGV(TAG, "Byte %d bit %d fail", pos, bit);
300 return false;
301 }
302 }
303 state_frame[pos] = byte;
304
305 // Check Header && Footer
306 if ((pos == 0 && byte != MITSUBISHI_BYTE00) || (pos == 1 && byte != MITSUBISHI_BYTE01) ||
307 (pos == 2 && byte != MITSUBISHI_BYTE02) || (pos == 3 && byte != MITSUBISHI_BYTE03) ||
308 (pos == 4 && byte != MITSUBISHI_BYTE04) || (pos == 13 && byte != MITSUBISHI_BYTE13) ||
309 (pos == 16 && byte != MITSUBISHI_BYTE16)) {
310 ESP_LOGV(TAG, "Bytes 0,1,2,3,4,13 or 16 fail - invalid value");
311 return false;
312 }
313 }
314
315 // On/Off and Mode
316 if (state_frame[5] == MITSUBISHI_OFF) {
318 } else {
319 switch (state_frame[6]) {
322 break;
325 break;
328 break;
331 break;
334 break;
335 }
336 }
337
338 // Temp
339 this->target_temperature = state_frame[7] + MITSUBISHI_TEMP_MIN;
340
341 // Fan
342 uint8_t fan = state_frame[9] & 0x07; //(Bit 0,1,2 = Speed)
343 // Map of Climate fan mode to this device expected value
344 // For 3Level: Low = 1, Medium = 2, High = 3
345 // For 4Level: Low = 1, Middle = 2, Medium = 3, High = 4
346 // For 5Level: Low = 1, Middle = 2, Medium = 3, High = 4
347 // For 4Level + Quiet: Low = 1, Middle = 2, Medium = 3, High = 4, Quiet = 5
348 climate::ClimateFanMode modes_mapping[8] = {
357 this->fan_mode = modes_mapping[fan];
358
359 // Wide Vane
360 uint8_t wide_vane = state_frame[8] & 0xF0; // Bits 4,5,6,7
361 switch (wide_vane) {
364 break;
365 default:
367 break;
368 }
369
370 // Vertical Vane
371 uint8_t vertical_vane = state_frame[9] & 0x38; // Bits 3,4,5
372 switch (vertical_vane) {
376 } else {
378 }
379 break;
380 }
381
382 switch (state_frame[14]) {
385 break;
388 break;
389 }
390
391 ESP_LOGV(TAG, "Receiving: %s", format_hex_pretty(state_frame, 18).c_str());
392
393 this->publish_state();
394 return true;
395}
396
397} // namespace mitsubishi
398} // namespace esphome
ClimateMode mode
The active mode of the climate device.
Definition climate.h:256
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:250
float target_temperature
The target temperature of the climate device.
Definition climate.h:237
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:262
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:426
optional< ClimatePreset > preset
The active preset of the climate device.
Definition climate.h:253
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
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, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.
Definition helpers.cpp:317