diff --git a/README.md b/README.md index 2db7556..d9dd57e 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ tested by the author on the following units: * `MSZ-GL06NA` * `MFZ-KA09NA` +* `MSZ-FH35V` +* `MSZ-LN35VG2W` ## Usage @@ -240,6 +242,11 @@ climate: - platform: mitsubishi_heatpump name: "${friendly_name}" + horizontal_vane_select: + name: Horizontal Vane + vertical_vane_select: + name: Vertical Vane + # ESP32 only - change UART0 to UART1 or UART2 and remove the # logging:baud_rate above to allow the built-in UART0 to function for # logging. @@ -327,6 +334,11 @@ climate: - platform: mitsubishi_heatpump name: "${friendly_name}" + horizontal_vane_select: + name: Horizontal Vane + vertical_vane_select: + name: Vertical Vane + # ESP32 only - change UART0 to UART1 or UART2 and remove the # logging:baud_rate above to allow the built-in UART0 to function for # logging. @@ -350,8 +362,7 @@ climate: supports: mode: ["HEAT_COOL", "COOL", "HEAT", "FAN_ONLY"] fan_mode: ["AUTO", "LOW", "MEDIUM", "HIGH"] - swing_mode: ["OFF", "VERTICAL"] - + swing_mode: ["OFF", "VERTICAL", "HORIZONTAL", "BOTH"] visual: min_temperature: 16 max_temperature: 31 @@ -397,6 +408,7 @@ climate: request wasn't received from your ESPHome controller. This will result in the heatpump reverting to it's internal temperature sensor if the heatpump loses it's WiFi connection. +equest.) ## Other configuration diff --git a/components/mitsubishi_heatpump/climate.py b/components/mitsubishi_heatpump/climate.py index 371e421..7c90ca3 100644 --- a/components/mitsubishi_heatpump/climate.py +++ b/components/mitsubishi_heatpump/climate.py @@ -1,6 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import climate +from esphome.components import climate, select from esphome.components.logger import HARDWARE_UART_TO_SERIAL from esphome.const import ( CONF_ID, @@ -15,12 +15,24 @@ from esphome.const import ( ) from esphome.core import CORE, coroutine -AUTO_LOAD = ["climate"] +AUTO_LOAD = ["climate", "select"] CONF_SUPPORTS = "supports" +CONF_HORIZONTAL_SWING_SELECT = "horizontal_vane_select" +CONF_VERTICAL_SWING_SELECT = "vertical_vane_select" DEFAULT_CLIMATE_MODES = ["HEAT_COOL", "COOL", "HEAT", "DRY", "FAN_ONLY"] DEFAULT_FAN_MODES = ["AUTO", "DIFFUSE", "LOW", "MEDIUM", "MIDDLE", "HIGH"] DEFAULT_SWING_MODES = ["OFF", "VERTICAL"] +HORIZONTAL_SWING_OPTIONS = [ + "auto", + "swing", + "left", + "left_center", + "center", + "right_center", + "right", +] +VERTICAL_SWING_OPTIONS = ["swing", "auto", "up", "up_center", "center", "down_center", "down"] # Remote temperature timeout configuration CONF_REMOTE_OPERATING_TIMEOUT = "remote_temperature_operating_timeout_minutes" @@ -31,6 +43,9 @@ MitsubishiHeatPump = cg.global_ns.class_( "MitsubishiHeatPump", climate.Climate, cg.PollingComponent ) +MitsubishiACSelect = cg.global_ns.class_( + "MitsubishiACSelect", select.Select, cg.Component +) def valid_uart(uart): if CORE.is_esp8266: @@ -43,6 +58,10 @@ def valid_uart(uart): return cv.one_of(*uarts, upper=True)(uart) +SELECT_SCHEMA = select.SELECT_SCHEMA.extend( + {cv.GenerateID(CONF_ID): cv.declare_id(MitsubishiACSelect)} +) + CONFIG_SCHEMA = climate.CLIMATE_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(MitsubishiHeatPump), @@ -58,6 +77,9 @@ CONFIG_SCHEMA = climate.CLIMATE_SCHEMA.extend( cv.Optional(CONF_UPDATE_INTERVAL, default="500ms"): cv.All( cv.update_interval, cv.Range(max=cv.TimePeriod(milliseconds=9000)) ), + # Add selects for vertical and horizontal vane positions + cv.Optional(CONF_HORIZONTAL_SWING_SELECT): SELECT_SCHEMA, + cv.Optional(CONF_VERTICAL_SWING_SELECT): SELECT_SCHEMA, # Optionally override the supported ClimateTraits. cv.Optional(CONF_SUPPORTS, default={}): cv.Schema( { @@ -113,6 +135,18 @@ def to_code(config): climate.CLIMATE_SWING_MODES[mode] )) + if CONF_HORIZONTAL_SWING_SELECT in config: + conf = config[CONF_HORIZONTAL_SWING_SELECT] + swing_select = yield select.new_select(conf, options=HORIZONTAL_SWING_OPTIONS) + yield cg.register_component(swing_select, conf) + cg.add(var.set_horizontal_vane_select(swing_select)) + + if CONF_VERTICAL_SWING_SELECT in config: + conf = config[CONF_VERTICAL_SWING_SELECT] + swing_select = yield select.new_select(conf, options=VERTICAL_SWING_OPTIONS) + yield cg.register_component(swing_select, conf) + cg.add(var.set_vertical_vane_select(swing_select)) + yield cg.register_component(var, config) yield climate.register_climate(var, config) cg.add_library( diff --git a/components/mitsubishi_heatpump/espmhp.cpp b/components/mitsubishi_heatpump/espmhp.cpp index 03748d9..0c9be9b 100644 --- a/components/mitsubishi_heatpump/espmhp.cpp +++ b/components/mitsubishi_heatpump/espmhp.cpp @@ -111,6 +111,117 @@ climate::ClimateTraits& MitsubishiHeatPump::config_traits() { return traits_; } +void MitsubishiHeatPump::update_swing_horizontal(const std::string &swing) { + this->horizontal_swing_state_ = swing; + + if (this->horizontal_vane_select_ != nullptr && + this->horizontal_vane_select_->state != this->horizontal_swing_state_) { + this->horizontal_vane_select_->publish_state( + this->horizontal_swing_state_); // Set current horizontal swing + // position + } +} + +void MitsubishiHeatPump::update_swing_vertical(const std::string &swing) { + this->vertical_swing_state_ = swing; + + if (this->vertical_vane_select_ != nullptr && + this->vertical_vane_select_->state != this->vertical_swing_state_) { + this->vertical_vane_select_->publish_state( + this->vertical_swing_state_); // Set current vertical swing position + } +} + +void MitsubishiHeatPump::set_vertical_vane_select( + select::Select *vertical_vane_select) { + this->vertical_vane_select_ = vertical_vane_select; + this->vertical_vane_select_->add_on_state_callback( + [this](const std::string &value, size_t index) { + if (value == this->vertical_swing_state_) return; + this->on_vertical_swing_change(value); + }); +} + +void MitsubishiHeatPump::set_horizontal_vane_select( + select::Select *horizontal_vane_select) { + this->horizontal_vane_select_ = horizontal_vane_select; + this->horizontal_vane_select_->add_on_state_callback( + [this](const std::string &value, size_t index) { + if (value == this->horizontal_swing_state_) return; + this->on_horizontal_swing_change(value); + }); +} + +void MitsubishiHeatPump::on_vertical_swing_change(const std::string &swing) { + ESP_LOGD(TAG, "Setting vertical swing position"); + bool updated = false; + + if (swing == "swing") { + hp->setVaneSetting("SWING"); + updated = true; + } else if (swing == "auto") { + hp->setVaneSetting("AUTO"); + updated = true; + } else if (swing == "up") { + hp->setVaneSetting("1"); + updated = true; + } else if (swing == "up_center") { + hp->setVaneSetting("2"); + updated = true; + } else if (swing == "center") { + hp->setVaneSetting("3"); + updated = true; + } else if (swing == "down_center") { + hp->setVaneSetting("4"); + updated = true; + } else if (swing == "down") { + hp->setVaneSetting("5"); + updated = true; + } else { + ESP_LOGW(TAG, "Invalid vertical vane position %s", swing); + } + + ESP_LOGD(TAG, "Vertical vane - Was HeatPump updated? %s", YESNO(updated)); + + // and the heat pump: + hp->update(); +} + +void MitsubishiHeatPump::on_horizontal_swing_change(const std::string &swing) { + ESP_LOGD(TAG, "Setting horizontal swing position"); + bool updated = false; + + if (swing == "swing") { + hp->setWideVaneSetting("SWING"); + updated = true; + } else if (swing == "auto") { + hp->setWideVaneSetting("<>"); + updated = true; + } else if (swing == "left") { + hp->setWideVaneSetting("<<"); + updated = true; + } else if (swing == "left_center") { + hp->setWideVaneSetting("<"); + updated = true; + } else if (swing == "center") { + hp->setWideVaneSetting("|"); + updated = true; + } else if (swing == "right_center") { + hp->setWideVaneSetting(">"); + updated = true; + } else if (swing == "right") { + hp->setWideVaneSetting(">>"); + updated = true; + } else { + ESP_LOGW(TAG, "Invalid horizontal vane position %s", swing); + } + + ESP_LOGD(TAG, "Horizontal vane - Was HeatPump updated? %s", YESNO(updated)); + + // and the heat pump: + hp->update(); + } + /** * Implement control of a MitsubishiHeatPump. * @@ -264,10 +375,22 @@ void MitsubishiHeatPump::control(const climate::ClimateCall &call) { switch(*call.get_swing_mode()) { case climate::CLIMATE_SWING_OFF: hp->setVaneSetting("AUTO"); + hp->setWideVaneSetting("|"); updated = true; break; case climate::CLIMATE_SWING_VERTICAL: hp->setVaneSetting("SWING"); + hp->setWideVaneSetting("|"); + updated = true; + break; + case climate::CLIMATE_SWING_HORIZONTAL: + hp->setVaneSetting("3"); + hp->setWideVaneSetting("SWING"); + updated = true; + break; + case climate::CLIMATE_SWING_BOTH: + hp->setVaneSetting("SWING"); + hp->setWideVaneSetting("SWING"); updated = true; break; default: @@ -370,15 +493,52 @@ void MitsubishiHeatPump::hpSettingsChanged() { /* ******** HANDLE MITSUBISHI VANE CHANGES ******** * const char* VANE_MAP[7] = {"AUTO", "1", "2", "3", "4", "5", "SWING"}; */ - if (strcmp(currentSettings.vane, "SWING") == 0) { + if (strcmp(currentSettings.vane, "SWING") == 0 && + strcmp(currentSettings.wideVane, "SWING") == 0) { + this->swing_mode = climate::CLIMATE_SWING_BOTH; + } else if (strcmp(currentSettings.vane, "SWING") == 0) { this->swing_mode = climate::CLIMATE_SWING_VERTICAL; - } - else { + } else if (strcmp(currentSettings.wideVane, "SWING") == 0) { + this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; + } else { this->swing_mode = climate::CLIMATE_SWING_OFF; } ESP_LOGI(TAG, "Swing mode is: %i", this->swing_mode); + if (strcmp(currentSettings.vane, "SWING") == 0) { + this->update_swing_vertical("swing"); + } else if (strcmp(currentSettings.vane, "AUTO") == 0) { + this->update_swing_vertical("auto"); + } else if (strcmp(currentSettings.vane, "1") == 0) { + this->update_swing_vertical("up"); + } else if (strcmp(currentSettings.vane, "2") == 0) { + this->update_swing_vertical("up_center"); + } else if (strcmp(currentSettings.vane, "3") == 0) { + this->update_swing_vertical("center"); + } else if (strcmp(currentSettings.vane, "4") == 0) { + this->update_swing_vertical("down_center"); + } else if (strcmp(currentSettings.vane, "5") == 0) { + this->update_swing_vertical("down"); + } + ESP_LOGI(TAG, "Vertical vane mode is: %s", currentSettings.vane); + if (strcmp(currentSettings.wideVane, "SWING") == 0) { + this->update_swing_horizontal("swing"); + } else if (strcmp(currentSettings.wideVane, "<>") == 0) { + this->update_swing_horizontal("auto"); + } else if (strcmp(currentSettings.wideVane, "<<") == 0) { + this->update_swing_horizontal("left"); + } else if (strcmp(currentSettings.wideVane, "<") == 0) { + this->update_swing_horizontal("left_center"); + } else if (strcmp(currentSettings.wideVane, "|") == 0) { + this->update_swing_horizontal("center"); + } else if (strcmp(currentSettings.wideVane, ">") == 0) { + this->update_swing_horizontal("right_center"); + } else if (strcmp(currentSettings.wideVane, ">>") == 0) { + this->update_swing_horizontal("right"); + } + + ESP_LOGI(TAG, "Horizontal vane mode is: %s", currentSettings.wideVane); /* * ******** HANDLE TARGET TEMPERATURE CHANGES ******** @@ -525,6 +685,8 @@ void MitsubishiHeatPump::setup() { this->target_temperature = NAN; this->fan_mode = climate::CLIMATE_FAN_OFF; this->swing_mode = climate::CLIMATE_SWING_OFF; + this->vertical_swing_state_ = "auto"; + this->horizontal_swing_state_ = "auto"; #ifdef USE_CALLBACKS hp->setSettingsChangedCallback( diff --git a/components/mitsubishi_heatpump/espmhp.h b/components/mitsubishi_heatpump/espmhp.h index 5eb31af..7d2b582 100644 --- a/components/mitsubishi_heatpump/espmhp.h +++ b/components/mitsubishi_heatpump/espmhp.h @@ -18,6 +18,7 @@ #define USE_CALLBACKS #include "esphome.h" +#include "esphome/components/select/select.h" #include "esphome/core/preferences.h" #include @@ -100,6 +101,9 @@ class MitsubishiHeatPump : public esphome::PollingComponent, public esphome::cli // set_remote_temp(0) to switch back to the internal sensor. void set_remote_temperature(float); + void set_vertical_vane_select(esphome::select::Select *vertical_vane_select); + void set_horizontal_vane_select(esphome::select::Select *horizontal_vane_select); + // Used to validate that a connection is present between the controller // and this heatpump. void ping(); @@ -123,6 +127,12 @@ class MitsubishiHeatPump : public esphome::PollingComponent, public esphome::cli // The ClimateTraits supported by this HeatPump. esphome::climate::ClimateTraits traits_; + // Vane position + void update_swing_horizontal(const std::string &swing); + void update_swing_vertical(const std::string &swing); + std::string vertical_swing_state_; + std::string horizontal_swing_state_; + // Allow the HeatPump class to use get_hw_serial_ friend class HeatPump; @@ -148,6 +158,15 @@ class MitsubishiHeatPump : public esphome::PollingComponent, public esphome::cli static void save(float value, esphome::ESPPreferenceObject& storage); static esphome::optional load(esphome::ESPPreferenceObject& storage); + esphome::select::Select *vertical_vane_select_ = + nullptr; // Select to store manual position of vertical swing + esphome::select::Select *horizontal_vane_select_ = + nullptr; // Select to store manual position of horizontal swing + + // When received command to change the vane positions + void on_horizontal_swing_change(const std::string &swing); + void on_vertical_swing_change(const std::string &swing); + static void log_packet(byte* packet, unsigned int length, char* packetDirection); private: diff --git a/components/mitsubishi_heatpump/mitsubishi_ac_select.h b/components/mitsubishi_heatpump/mitsubishi_ac_select.h new file mode 100644 index 0000000..ceab5b2 --- /dev/null +++ b/components/mitsubishi_heatpump/mitsubishi_ac_select.h @@ -0,0 +1,15 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "esphome/core/component.h" + +namespace esphome { + +class MitsubishiACSelect : public select::Select, public Component { + protected: + void control(const std::string &value) override { + this->publish_state(value); + } +}; + +} // namespace esphome