mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Mitsubishi Climate updates (#3886)
Co-authored-by: Blair McBride <blair@theunfocused.net> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: RubyBailey <ruby_bailey11@hotmail.com> Co-authored-by: X-Ryl669 <boite.pour.spam@gmail.com> Co-authored-by: OlympusMonds <OlympusMonds@users.noreply.github.com>
This commit is contained in:
		| @@ -9,9 +9,53 @@ AUTO_LOAD = ["climate_ir"] | |||||||
| mitsubishi_ns = cg.esphome_ns.namespace("mitsubishi") | mitsubishi_ns = cg.esphome_ns.namespace("mitsubishi") | ||||||
| MitsubishiClimate = mitsubishi_ns.class_("MitsubishiClimate", climate_ir.ClimateIR) | MitsubishiClimate = mitsubishi_ns.class_("MitsubishiClimate", climate_ir.ClimateIR) | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend( | CONF_SET_FAN_MODE = "set_fan_mode" | ||||||
|  | SetFanMode = mitsubishi_ns.enum("SetFanMode") | ||||||
|  | SETFANMODE = { | ||||||
|  |     "quiet_4levels": SetFanMode.MITSUBISHI_FAN_Q4L, | ||||||
|  |     #    "5levels": SetFanMode.MITSUBISHI_FAN_5L, | ||||||
|  |     "4levels": SetFanMode.MITSUBISHI_FAN_4L, | ||||||
|  |     "3levels": SetFanMode.MITSUBISHI_FAN_3L, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CONF_SUPPORTS_DRY = "supports_dry" | ||||||
|  | CONF_SUPPORTS_FAN_ONLY = "supports_fan_only" | ||||||
|  |  | ||||||
|  | CONF_HORIZONTAL_DEFAULT = "horizontal_default" | ||||||
|  | HorizontalDirections = mitsubishi_ns.enum("HorizontalDirections") | ||||||
|  | HORIZONTAL_DIRECTIONS = { | ||||||
|  |     "left": HorizontalDirections.HORIZONTAL_DIRECTION_LEFT, | ||||||
|  |     "middle-left": HorizontalDirections.HORIZONTAL_DIRECTION_MIDDLE_LEFT, | ||||||
|  |     "middle": HorizontalDirections.HORIZONTAL_DIRECTION_MIDDLE, | ||||||
|  |     "middle-right": HorizontalDirections.HORIZONTAL_DIRECTION_MIDDLE_RIGHT, | ||||||
|  |     "right": HorizontalDirections.HORIZONTAL_DIRECTION_RIGHT, | ||||||
|  |     "split": HorizontalDirections.HORIZONTAL_DIRECTION_SPLIT, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CONF_VERTICAL_DEFAULT = "vertical_default" | ||||||
|  | VerticalDirections = mitsubishi_ns.enum("VerticalDirections") | ||||||
|  | VERTICAL_DIRECTIONS = { | ||||||
|  |     "auto": VerticalDirections.VERTICAL_DIRECTION_AUTO, | ||||||
|  |     "up": VerticalDirections.VERTICAL_DIRECTION_UP, | ||||||
|  |     "middle-up": VerticalDirections.VERTICAL_DIRECTION_MIDDLE_UP, | ||||||
|  |     "middle": VerticalDirections.VERTICAL_DIRECTION_MIDDLE, | ||||||
|  |     "middle-down": VerticalDirections.VERTICAL_DIRECTION_MIDDLE_DOWN, | ||||||
|  |     "down": VerticalDirections.VERTICAL_DIRECTION_DOWN, | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( | ||||||
|     { |     { | ||||||
|         cv.GenerateID(): cv.declare_id(MitsubishiClimate), |         cv.GenerateID(): cv.declare_id(MitsubishiClimate), | ||||||
|  |         cv.Optional(CONF_SET_FAN_MODE, default="3levels"): cv.enum(SETFANMODE), | ||||||
|  |         cv.Optional(CONF_SUPPORTS_DRY, default=False): cv.boolean, | ||||||
|  |         cv.Optional(CONF_SUPPORTS_FAN_ONLY, default=False): cv.boolean, | ||||||
|  |         cv.Optional(CONF_HORIZONTAL_DEFAULT, default="middle"): cv.enum( | ||||||
|  |             HORIZONTAL_DIRECTIONS | ||||||
|  |         ), | ||||||
|  |         cv.Optional(CONF_VERTICAL_DEFAULT, default="middle"): cv.enum( | ||||||
|  |             VERTICAL_DIRECTIONS | ||||||
|  |         ), | ||||||
|     } |     } | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -19,3 +63,9 @@ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend( | |||||||
| async def to_code(config): | async def to_code(config): | ||||||
|     var = cg.new_Pvariable(config[CONF_ID]) |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|     await climate_ir.register_climate_ir(var, config) |     await climate_ir.register_climate_ir(var, config) | ||||||
|  |  | ||||||
|  |     cg.add(var.set_fan_mode(config[CONF_SET_FAN_MODE])) | ||||||
|  |     cg.add(var.set_supports_dry(config[CONF_SUPPORTS_DRY])) | ||||||
|  |     cg.add(var.set_supports_fan_only(config[CONF_SUPPORTS_FAN_ONLY])) | ||||||
|  |     cg.add(var.set_horizontal_default(config[CONF_HORIZONTAL_DEFAULT])) | ||||||
|  |     cg.add(var.set_vertical_default(config[CONF_VERTICAL_DEFAULT])) | ||||||
|   | |||||||
| @@ -8,12 +8,31 @@ static const char *const TAG = "mitsubishi.climate"; | |||||||
|  |  | ||||||
| const uint32_t MITSUBISHI_OFF = 0x00; | const uint32_t MITSUBISHI_OFF = 0x00; | ||||||
|  |  | ||||||
| const uint8_t MITSUBISHI_COOL = 0x18; | const uint8_t MITSUBISHI_MODE_AUTO = 0x20; | ||||||
| const uint8_t MITSUBISHI_DRY = 0x10; | const uint8_t MITSUBISHI_MODE_COOL = 0x18; | ||||||
| const uint8_t MITSUBISHI_AUTO = 0x20; | const uint8_t MITSUBISHI_MODE_DRY = 0x10; | ||||||
| const uint8_t MITSUBISHI_HEAT = 0x08; | const uint8_t MITSUBISHI_MODE_FAN_ONLY = 0x38; | ||||||
|  | const uint8_t MITSUBISHI_MODE_HEAT = 0x08; | ||||||
|  |  | ||||||
|  | const uint8_t MITSUBISHI_MODE_A_HEAT = 0x00; | ||||||
|  | const uint8_t MITSUBISHI_MODE_A_DRY = 0x02; | ||||||
|  | const uint8_t MITSUBISHI_MODE_A_COOL = 0x06; | ||||||
|  | const uint8_t MITSUBISHI_MODE_A_AUTO = 0x06; | ||||||
|  |  | ||||||
|  | const uint8_t MITSUBISHI_WIDE_VANE_SWING = 0xC0; | ||||||
|  |  | ||||||
| const uint8_t MITSUBISHI_FAN_AUTO = 0x00; | const uint8_t MITSUBISHI_FAN_AUTO = 0x00; | ||||||
|  |  | ||||||
|  | const uint8_t MITSUBISHI_VERTICAL_VANE_SWING = 0x38; | ||||||
|  |  | ||||||
|  | // const uint8_t MITSUBISHI_AUTO = 0X80; | ||||||
|  | const uint8_t MITSUBISHI_OTHERWISE = 0X40; | ||||||
|  | const uint8_t MITSUBISHI_POWERFUL = 0x08; | ||||||
|  |  | ||||||
|  | // Optional presets used to enable some model features | ||||||
|  | const uint8_t MITSUBISHI_ECONOCOOL = 0x20; | ||||||
|  | const uint8_t MITSUBISHI_NIGHTMODE = 0xC1; | ||||||
|  |  | ||||||
| // Pulse parameters in usec | // Pulse parameters in usec | ||||||
| const uint16_t MITSUBISHI_BIT_MARK = 430; | const uint16_t MITSUBISHI_BIT_MARK = 430; | ||||||
| const uint16_t MITSUBISHI_ONE_SPACE = 1250; | const uint16_t MITSUBISHI_ONE_SPACE = 1250; | ||||||
| @@ -22,19 +41,97 @@ const uint16_t MITSUBISHI_HEADER_MARK = 3500; | |||||||
| const uint16_t MITSUBISHI_HEADER_SPACE = 1700; | const uint16_t MITSUBISHI_HEADER_SPACE = 1700; | ||||||
| const uint16_t MITSUBISHI_MIN_GAP = 17500; | const uint16_t MITSUBISHI_MIN_GAP = 17500; | ||||||
|  |  | ||||||
|  | // Marker bytes | ||||||
|  | const uint8_t MITSUBISHI_BYTE00 = 0X23; | ||||||
|  | const uint8_t MITSUBISHI_BYTE01 = 0XCB; | ||||||
|  | const uint8_t MITSUBISHI_BYTE02 = 0X26; | ||||||
|  | const uint8_t MITSUBISHI_BYTE03 = 0X01; | ||||||
|  | const uint8_t MITSUBISHI_BYTE04 = 0X00; | ||||||
|  | const uint8_t MITSUBISHI_BYTE13 = 0X00; | ||||||
|  | const uint8_t MITSUBISHI_BYTE16 = 0X00; | ||||||
|  |  | ||||||
|  | climate::ClimateTraits MitsubishiClimate::traits() { | ||||||
|  |   auto traits = climate::ClimateTraits(); | ||||||
|  |   traits.set_supports_action(false); | ||||||
|  |   traits.set_visual_min_temperature(MITSUBISHI_TEMP_MIN); | ||||||
|  |   traits.set_visual_max_temperature(MITSUBISHI_TEMP_MAX); | ||||||
|  |   traits.set_visual_temperature_step(1.0f); | ||||||
|  |   traits.set_supported_modes({climate::CLIMATE_MODE_OFF}); | ||||||
|  |  | ||||||
|  |   if (this->supports_cool_) | ||||||
|  |     traits.add_supported_mode(climate::CLIMATE_MODE_COOL); | ||||||
|  |   if (this->supports_heat_) | ||||||
|  |     traits.add_supported_mode(climate::CLIMATE_MODE_HEAT); | ||||||
|  |  | ||||||
|  |   if (this->supports_cool_ && this->supports_heat_) | ||||||
|  |     traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); | ||||||
|  |  | ||||||
|  |   if (this->supports_dry_) | ||||||
|  |     traits.add_supported_mode(climate::CLIMATE_MODE_DRY); | ||||||
|  |   if (this->supports_fan_only_) | ||||||
|  |     traits.add_supported_mode(climate::CLIMATE_MODE_FAN_ONLY); | ||||||
|  |  | ||||||
|  |   // Default to only 3 levels in ESPHome even if most unit supports 4. The 3rd level is not used. | ||||||
|  |   traits.set_supported_fan_modes( | ||||||
|  |       {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH}); | ||||||
|  |   if (this->fan_mode_ == MITSUBISHI_FAN_Q4L) | ||||||
|  |     traits.add_supported_fan_mode(climate::CLIMATE_FAN_QUIET); | ||||||
|  |   if (/*this->fan_mode_ == MITSUBISHI_FAN_5L ||*/ this->fan_mode_ >= MITSUBISHI_FAN_4L) | ||||||
|  |     traits.add_supported_fan_mode(climate::CLIMATE_FAN_MIDDLE);  // Shouldn't be used for this but it helps | ||||||
|  |  | ||||||
|  |   traits.set_supported_swing_modes({climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, | ||||||
|  |                                     climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL}); | ||||||
|  |  | ||||||
|  |   traits.set_supported_presets({climate::CLIMATE_PRESET_NONE, climate::CLIMATE_PRESET_ECO, | ||||||
|  |                                 climate::CLIMATE_PRESET_BOOST, climate::CLIMATE_PRESET_SLEEP}); | ||||||
|  |  | ||||||
|  |   return traits; | ||||||
|  | } | ||||||
|  |  | ||||||
| void MitsubishiClimate::transmit_state() { | void MitsubishiClimate::transmit_state() { | ||||||
|   uint32_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x00, 0x30, |   // Byte 0-4: Constant: 0x23, 0xCB, 0x26, 0x01, 0x00 | ||||||
|                                0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |   // Byte 5: On=0x20, Off: 0x00 | ||||||
|  |   // Byte 6: MODE (See MODEs above (Heat/Dry/Cool/Auto/FanOnly) | ||||||
|  |   // Byte 7: TEMP bits 0,1,2,3, added to MITSUBISHI_TEMP_MIN | ||||||
|  |   //          Example: 0x00 = 0°C+MITSUBISHI_TEMP_MIN = 16°C; 0x07 = 7°C+MITSUBISHI_TEMP_MIN = 23°C | ||||||
|  |   // Byte 8: MODE_A & Wide Vane (if present) | ||||||
|  |   //          MODE_A bits 0,1,2 different than Byte 6 (See MODE_As above) | ||||||
|  |   //          Wide Vane bits 4,5,6,7 (Middle = 0x30) | ||||||
|  |   // Byte 9: FAN/Vertical Vane/Switch To Auto | ||||||
|  |   //          FAN (Speed) bits 0,1,2 | ||||||
|  |   //          Vertical Vane bits 3,4,5 (Auto = 0x00) | ||||||
|  |   //          Switch To Auto bits 6,7 | ||||||
|  |   // Byte 10: CLOCK Current time as configured on remote (0x00=Not used) | ||||||
|  |   // Byte 11: END CLOCK Stop time of HVAC (0x00 for no setting) | ||||||
|  |   // Byte 12: START CLOCK Start time of HVAC (0x00 for no setting) | ||||||
|  |   // Byte 13: Constant 0x00 | ||||||
|  |   // Byte 14: HVAC specfic, i.e. ECONO COOL, CLEAN MODE, always 0x00 | ||||||
|  |   // Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00 | ||||||
|  |   // Byte 16: Constant 0x00 | ||||||
|  |   // Byte 17: Checksum: SUM[Byte0...Byte16] | ||||||
|  |   uint32_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x00, 0x00, | ||||||
|  |                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||||||
|  |  | ||||||
|   switch (this->mode) { |   switch (this->mode) { | ||||||
|     case climate::CLIMATE_MODE_COOL: |  | ||||||
|       remote_state[6] = MITSUBISHI_COOL; |  | ||||||
|       break; |  | ||||||
|     case climate::CLIMATE_MODE_HEAT: |     case climate::CLIMATE_MODE_HEAT: | ||||||
|       remote_state[6] = MITSUBISHI_HEAT; |       remote_state[6] = MITSUBISHI_MODE_HEAT; | ||||||
|  |       remote_state[8] = MITSUBISHI_MODE_A_HEAT; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_DRY: | ||||||
|  |       remote_state[6] = MITSUBISHI_MODE_DRY; | ||||||
|  |       remote_state[8] = MITSUBISHI_MODE_A_DRY; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_COOL: | ||||||
|  |       remote_state[6] = MITSUBISHI_MODE_COOL; | ||||||
|  |       remote_state[8] = MITSUBISHI_MODE_A_COOL; | ||||||
|       break; |       break; | ||||||
|     case climate::CLIMATE_MODE_HEAT_COOL: |     case climate::CLIMATE_MODE_HEAT_COOL: | ||||||
|       remote_state[6] = MITSUBISHI_AUTO; |       remote_state[6] = MITSUBISHI_MODE_AUTO; | ||||||
|  |       remote_state[8] = MITSUBISHI_MODE_A_AUTO; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_MODE_FAN_ONLY: | ||||||
|  |       remote_state[6] = MITSUBISHI_MODE_FAN_ONLY; | ||||||
|  |       remote_state[8] = MITSUBISHI_MODE_A_AUTO; | ||||||
|       break; |       break; | ||||||
|     case climate::CLIMATE_MODE_OFF: |     case climate::CLIMATE_MODE_OFF: | ||||||
|     default: |     default: | ||||||
| @@ -42,17 +139,111 @@ void MitsubishiClimate::transmit_state() { | |||||||
|       break; |       break; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   remote_state[7] = (uint8_t) roundf(clamp<float>(this->target_temperature, MITSUBISHI_TEMP_MIN, MITSUBISHI_TEMP_MAX) - |   // Temperature | ||||||
|                                      MITSUBISHI_TEMP_MIN); |   if (this->mode == climate::CLIMATE_MODE_DRY) { | ||||||
|  |     remote_state[7] = 24 - MITSUBISHI_TEMP_MIN;  // Remote sends always 24°C if "Dry" mode is selected | ||||||
|  |   } else { | ||||||
|  |     remote_state[7] = (uint8_t) roundf( | ||||||
|  |         clamp<float>(this->target_temperature, MITSUBISHI_TEMP_MIN, MITSUBISHI_TEMP_MAX) - MITSUBISHI_TEMP_MIN); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   ESP_LOGV(TAG, "Sending Mitsubishi target temp: %.1f state: %02" PRIX32 " mode: %02" PRIX32 " temp: %02" PRIX32, |   // Wide Vane | ||||||
|            this->target_temperature, remote_state[5], remote_state[6], remote_state[7]); |   switch (this->swing_mode) { | ||||||
|  |     case climate::CLIMATE_SWING_HORIZONTAL: | ||||||
|  |     case climate::CLIMATE_SWING_BOTH: | ||||||
|  |       remote_state[8] = remote_state[8] | MITSUBISHI_WIDE_VANE_SWING;  // Wide Vane Swing | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_SWING_OFF: | ||||||
|  |     default: | ||||||
|  |       remote_state[8] = remote_state[8] | this->default_horizontal_direction_;  // Off--> horizontal default position | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGD(TAG, "default_horizontal_direction_: %02X", this->default_horizontal_direction_); | ||||||
|  |  | ||||||
|  |   // Fan Speed & Vertical Vane | ||||||
|  |   // Map of Climate fan mode to this device expected value | ||||||
|  |   // For 3Level: Low = 1, Medium = 2, High = 3 | ||||||
|  |   // For 4Level: Low = 1, Middle = 2, Medium = 3, High = 4 | ||||||
|  |   // For 5Level: Low = 1, Middle = 2, Medium = 3, High = 4 | ||||||
|  |   // For 4Level + Quiet: Low = 1, Middle = 2, Medium = 3, High = 4, Quiet = 5 | ||||||
|  |  | ||||||
|  |   switch (this->fan_mode.value()) { | ||||||
|  |     case climate::CLIMATE_FAN_LOW: | ||||||
|  |       remote_state[9] = 1; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_FAN_MEDIUM: | ||||||
|  |       if (this->fan_mode_ == MITSUBISHI_FAN_3L) { | ||||||
|  |         remote_state[9] = 2; | ||||||
|  |       } else { | ||||||
|  |         remote_state[9] = 3; | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_FAN_HIGH: | ||||||
|  |       if (this->fan_mode_ == MITSUBISHI_FAN_3L) { | ||||||
|  |         remote_state[9] = 3; | ||||||
|  |       } else { | ||||||
|  |         remote_state[9] = 4; | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_FAN_MIDDLE: | ||||||
|  |       remote_state[9] = 2; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_FAN_QUIET: | ||||||
|  |       remote_state[9] = 5; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       remote_state[9] = MITSUBISHI_FAN_AUTO; | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGD(TAG, "fan: %02x state: %02x", this->fan_mode.value(), remote_state[9]); | ||||||
|  |  | ||||||
|  |   // Vertical Vane | ||||||
|  |   switch (this->swing_mode) { | ||||||
|  |     case climate::CLIMATE_SWING_VERTICAL: | ||||||
|  |     case climate::CLIMATE_SWING_BOTH: | ||||||
|  |       remote_state[9] = remote_state[9] | MITSUBISHI_VERTICAL_VANE_SWING | MITSUBISHI_OTHERWISE;  // Vane Swing | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_SWING_OFF: | ||||||
|  |     default: | ||||||
|  |       remote_state[9] = remote_state[9] | this->default_vertical_direction_ | | ||||||
|  |                         MITSUBISHI_OTHERWISE;  // Off--> vertical default position | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGD(TAG, "default_vertical_direction_: %02X", this->default_vertical_direction_); | ||||||
|  |  | ||||||
|  |   // Special modes | ||||||
|  |   switch (this->preset.value()) { | ||||||
|  |     case climate::CLIMATE_PRESET_ECO: | ||||||
|  |       remote_state[6] = MITSUBISHI_MODE_COOL | MITSUBISHI_OTHERWISE; | ||||||
|  |       remote_state[8] = (remote_state[8] & ~7) | MITSUBISHI_MODE_A_COOL; | ||||||
|  |       remote_state[14] = MITSUBISHI_ECONOCOOL; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_PRESET_SLEEP: | ||||||
|  |       remote_state[9] = MITSUBISHI_FAN_AUTO; | ||||||
|  |       remote_state[14] = MITSUBISHI_NIGHTMODE; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_PRESET_BOOST: | ||||||
|  |       remote_state[6] |= MITSUBISHI_OTHERWISE; | ||||||
|  |       remote_state[15] = MITSUBISHI_POWERFUL; | ||||||
|  |       break; | ||||||
|  |     case climate::CLIMATE_PRESET_NONE: | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // Checksum |   // Checksum | ||||||
|   for (int i = 0; i < 17; i++) { |   for (int i = 0; i < 17; i++) { | ||||||
|     remote_state[17] += remote_state[i]; |     remote_state[17] += remote_state[i]; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGD(TAG, "sending: %02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X", | ||||||
|  |            remote_state[0], remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], | ||||||
|  |            remote_state[6], remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], | ||||||
|  |            remote_state[12], remote_state[13], remote_state[14], remote_state[15], remote_state[16], remote_state[17]); | ||||||
|  |  | ||||||
|   auto transmit = this->transmitter_->transmit(); |   auto transmit = this->transmitter_->transmit(); | ||||||
|   auto *data = transmit.get_data(); |   auto *data = transmit.get_data(); | ||||||
|  |  | ||||||
| @@ -81,5 +272,119 @@ void MitsubishiClimate::transmit_state() { | |||||||
|   transmit.perform(); |   transmit.perform(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool MitsubishiClimate::parse_state_frame_(const uint8_t frame[]) { return false; } | ||||||
|  |  | ||||||
|  | bool MitsubishiClimate::on_receive(remote_base::RemoteReceiveData data) { | ||||||
|  |   uint8_t state_frame[18] = {}; | ||||||
|  |  | ||||||
|  |   if (!data.expect_item(MITSUBISHI_HEADER_MARK, MITSUBISHI_HEADER_SPACE)) { | ||||||
|  |     ESP_LOGV(TAG, "Header fail"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   for (uint8_t pos = 0; pos < 18; pos++) { | ||||||
|  |     uint8_t byte = 0; | ||||||
|  |     for (int8_t bit = 0; bit < 8; bit++) { | ||||||
|  |       if (data.expect_item(MITSUBISHI_BIT_MARK, MITSUBISHI_ONE_SPACE)) { | ||||||
|  |         byte |= 1 << bit; | ||||||
|  |       } else if (!data.expect_item(MITSUBISHI_BIT_MARK, MITSUBISHI_ZERO_SPACE)) { | ||||||
|  |         ESP_LOGV(TAG, "Byte %d bit %d fail", pos, bit); | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     state_frame[pos] = byte; | ||||||
|  |  | ||||||
|  |     // Check Header && Footer | ||||||
|  |     if ((pos == 0 && byte != MITSUBISHI_BYTE00) || (pos == 1 && byte != MITSUBISHI_BYTE01) || | ||||||
|  |         (pos == 2 && byte != MITSUBISHI_BYTE02) || (pos == 3 && byte != MITSUBISHI_BYTE03) || | ||||||
|  |         (pos == 4 && byte != MITSUBISHI_BYTE04) || (pos == 13 && byte != MITSUBISHI_BYTE13) || | ||||||
|  |         (pos == 16 && byte != MITSUBISHI_BYTE16)) { | ||||||
|  |       ESP_LOGV(TAG, "Bytes 0,1,2,3,4,13 or 16 fail - invalid value"); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // On/Off and Mode | ||||||
|  |   if (state_frame[5] == MITSUBISHI_OFF) { | ||||||
|  |     this->mode = climate::CLIMATE_MODE_OFF; | ||||||
|  |   } else { | ||||||
|  |     switch (state_frame[6]) { | ||||||
|  |       case MITSUBISHI_MODE_HEAT: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_HEAT; | ||||||
|  |         break; | ||||||
|  |       case MITSUBISHI_MODE_DRY: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_DRY; | ||||||
|  |         break; | ||||||
|  |       case MITSUBISHI_MODE_COOL: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_COOL; | ||||||
|  |         break; | ||||||
|  |       case MITSUBISHI_MODE_FAN_ONLY: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_FAN_ONLY; | ||||||
|  |         break; | ||||||
|  |       case MITSUBISHI_MODE_AUTO: | ||||||
|  |         this->mode = climate::CLIMATE_MODE_HEAT_COOL; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Temp | ||||||
|  |   this->target_temperature = state_frame[7] + MITSUBISHI_TEMP_MIN; | ||||||
|  |  | ||||||
|  |   // Fan | ||||||
|  |   uint8_t fan = state_frame[9] & 0x07;  //(Bit 0,1,2 = Speed) | ||||||
|  |   // Map of Climate fan mode to this device expected value | ||||||
|  |   // For 3Level: Low = 1, Medium = 2, High = 3 | ||||||
|  |   // For 4Level: Low = 1, Middle = 2, Medium = 3, High = 4 | ||||||
|  |   // For 5Level: Low = 1, Middle = 2, Medium = 3, High = 4 | ||||||
|  |   // For 4Level + Quiet: Low = 1, Middle = 2, Medium = 3, High = 4, Quiet = 5 | ||||||
|  |   climate::ClimateFanMode modes_mapping[8] = { | ||||||
|  |       climate::CLIMATE_FAN_AUTO, | ||||||
|  |       climate::CLIMATE_FAN_LOW, | ||||||
|  |       this->fan_mode_ == MITSUBISHI_FAN_3L ? climate::CLIMATE_FAN_MEDIUM : climate::CLIMATE_FAN_MIDDLE, | ||||||
|  |       this->fan_mode_ == MITSUBISHI_FAN_3L ? climate::CLIMATE_FAN_HIGH : climate::CLIMATE_FAN_MEDIUM, | ||||||
|  |       climate::CLIMATE_FAN_HIGH, | ||||||
|  |       climate::CLIMATE_FAN_QUIET, | ||||||
|  |       climate::CLIMATE_FAN_AUTO, | ||||||
|  |       climate::CLIMATE_FAN_AUTO}; | ||||||
|  |   this->fan_mode = modes_mapping[fan]; | ||||||
|  |  | ||||||
|  |   // Wide Vane | ||||||
|  |   uint8_t wide_vane = state_frame[8] & 0xF0;  // Bits 4,5,6,7 | ||||||
|  |   switch (wide_vane) { | ||||||
|  |     case MITSUBISHI_WIDE_VANE_SWING: | ||||||
|  |       this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       this->swing_mode = climate::CLIMATE_SWING_OFF; | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Vertical Vane | ||||||
|  |   uint8_t vertical_vane = state_frame[9] & 0x38;  // Bits 3,4,5 | ||||||
|  |   switch (vertical_vane) { | ||||||
|  |     case MITSUBISHI_VERTICAL_VANE_SWING: | ||||||
|  |       if (this->swing_mode == climate::CLIMATE_SWING_HORIZONTAL) { | ||||||
|  |         this->swing_mode = climate::CLIMATE_SWING_BOTH; | ||||||
|  |       } else { | ||||||
|  |         this->swing_mode = climate::CLIMATE_SWING_VERTICAL; | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   switch (state_frame[14]) { | ||||||
|  |     case MITSUBISHI_ECONOCOOL: | ||||||
|  |       this->preset = climate::CLIMATE_PRESET_ECO; | ||||||
|  |       break; | ||||||
|  |     case MITSUBISHI_NIGHTMODE: | ||||||
|  |       this->preset = climate::CLIMATE_PRESET_SLEEP; | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   ESP_LOGV(TAG, "Receiving: %s", format_hex_pretty(state_frame, 18).c_str()); | ||||||
|  |  | ||||||
|  |   this->publish_state(); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
| }  // namespace mitsubishi | }  // namespace mitsubishi | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -11,13 +11,72 @@ namespace mitsubishi { | |||||||
| const uint8_t MITSUBISHI_TEMP_MIN = 16;  // Celsius | const uint8_t MITSUBISHI_TEMP_MIN = 16;  // Celsius | ||||||
| const uint8_t MITSUBISHI_TEMP_MAX = 31;  // Celsius | const uint8_t MITSUBISHI_TEMP_MAX = 31;  // Celsius | ||||||
|  |  | ||||||
|  | // Fan mode | ||||||
|  | enum SetFanMode { | ||||||
|  |   MITSUBISHI_FAN_3L = 0,  // 3 levels + auto | ||||||
|  |   MITSUBISHI_FAN_4L,      // 4 levels + auto | ||||||
|  |   MITSUBISHI_FAN_Q4L,     // Quiet + 4 levels + auto | ||||||
|  |   //  MITSUBISHI_FAN_5L,      // 5 levels + auto | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Enum to represent horizontal directios | ||||||
|  | enum HorizontalDirection { | ||||||
|  |   HORIZONTAL_DIRECTION_LEFT = 0x10, | ||||||
|  |   HORIZONTAL_DIRECTION_MIDDLE_LEFT = 0x20, | ||||||
|  |   HORIZONTAL_DIRECTION_MIDDLE = 0x30, | ||||||
|  |   HORIZONTAL_DIRECTION_MIDDLE_RIGHT = 0x40, | ||||||
|  |   HORIZONTAL_DIRECTION_RIGHT = 0x50, | ||||||
|  |   HORIZONTAL_DIRECTION_SPLIT = 0x80, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // Enum to represent vertical directions | ||||||
|  | enum VerticalDirection { | ||||||
|  |   VERTICAL_DIRECTION_AUTO = 0x00, | ||||||
|  |   VERTICAL_DIRECTION_UP = 0x08, | ||||||
|  |   VERTICAL_DIRECTION_MIDDLE_UP = 0x10, | ||||||
|  |   VERTICAL_DIRECTION_MIDDLE = 0x18, | ||||||
|  |   VERTICAL_DIRECTION_MIDDLE_DOWN = 0x20, | ||||||
|  |   VERTICAL_DIRECTION_DOWN = 0x28, | ||||||
|  | }; | ||||||
|  |  | ||||||
| class MitsubishiClimate : public climate_ir::ClimateIR { | class MitsubishiClimate : public climate_ir::ClimateIR { | ||||||
|  public: |  public: | ||||||
|   MitsubishiClimate() : climate_ir::ClimateIR(MITSUBISHI_TEMP_MIN, MITSUBISHI_TEMP_MAX) {} |   MitsubishiClimate() | ||||||
|  |       : climate_ir::ClimateIR(MITSUBISHI_TEMP_MIN, MITSUBISHI_TEMP_MAX, 1.0f, true, true, | ||||||
|  |                               {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MIDDLE, | ||||||
|  |                                climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH, climate::CLIMATE_FAN_QUIET}, | ||||||
|  |                               {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL, | ||||||
|  |                                climate::CLIMATE_SWING_HORIZONTAL}, | ||||||
|  |                               {climate::CLIMATE_PRESET_NONE, climate::CLIMATE_PRESET_ECO, climate::CLIMATE_PRESET_BOOST, | ||||||
|  |                                climate::CLIMATE_PRESET_SLEEP}) {} | ||||||
|  |  | ||||||
|  |   void set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; } | ||||||
|  |   void set_supports_dry(bool supports_dry) { this->supports_dry_ = supports_dry; } | ||||||
|  |   void set_supports_fan_only(bool supports_fan_only) { this->supports_fan_only_ = supports_fan_only; } | ||||||
|  |   void set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } | ||||||
|  |  | ||||||
|  |   void set_fan_mode(SetFanMode fan_mode) { this->fan_mode_ = fan_mode; } | ||||||
|  |  | ||||||
|  |   void set_horizontal_default(HorizontalDirection horizontal_direction) { | ||||||
|  |     this->default_horizontal_direction_ = horizontal_direction; | ||||||
|  |   } | ||||||
|  |   void set_vertical_default(VerticalDirection vertical_direction) { | ||||||
|  |     this->default_vertical_direction_ = vertical_direction; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   /// Transmit via IR the state of this climate controller. |   // Transmit via IR the state of this climate controller. | ||||||
|   void transmit_state() override; |   void transmit_state() override; | ||||||
|  |   // Handle received IR Buffer | ||||||
|  |   bool on_receive(remote_base::RemoteReceiveData data) override; | ||||||
|  |   bool parse_state_frame_(const uint8_t frame[]); | ||||||
|  |  | ||||||
|  |   SetFanMode fan_mode_; | ||||||
|  |  | ||||||
|  |   HorizontalDirection default_horizontal_direction_; | ||||||
|  |   VerticalDirection default_vertical_direction_; | ||||||
|  |  | ||||||
|  |   climate::ClimateTraits traits() override; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace mitsubishi | }  // namespace mitsubishi | ||||||
|   | |||||||
| @@ -2652,6 +2652,10 @@ climate: | |||||||
|     name: Yashima Climate |     name: Yashima Climate | ||||||
|   - platform: mitsubishi |   - platform: mitsubishi | ||||||
|     name: Mitsubishi |     name: Mitsubishi | ||||||
|  |     supports_dry: "true" | ||||||
|  |     supports_fan_only: "true" | ||||||
|  |     horizontal_default: "left" | ||||||
|  |     vertical_default: "down" | ||||||
|   - platform: whirlpool |   - platform: whirlpool | ||||||
|     name: Whirlpool Climate |     name: Whirlpool Climate | ||||||
|   - platform: climate_ir_lg |   - platform: climate_ir_lg | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user