Merge pull request #109 from nathanjw/unformat-select

Add vertical and horizontal vane position controls
This commit is contained in:
Geoff Davis 2024-06-23 13:49:30 -07:00 committed by GitHub
commit 2ac56d5a8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 249 additions and 7 deletions

View File

@ -42,6 +42,8 @@ tested by the author on the following units:
* `MSZ-GL06NA` * `MSZ-GL06NA`
* `MFZ-KA09NA` * `MFZ-KA09NA`
* `MSZ-FH35V`
* `MSZ-LN35VG2W`
## Usage ## Usage
@ -240,6 +242,11 @@ climate:
- platform: mitsubishi_heatpump - platform: mitsubishi_heatpump
name: "${friendly_name}" 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 # 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:baud_rate above to allow the built-in UART0 to function for
# logging. # logging.
@ -327,6 +334,11 @@ climate:
- platform: mitsubishi_heatpump - platform: mitsubishi_heatpump
name: "${friendly_name}" 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 # 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:baud_rate above to allow the built-in UART0 to function for
# logging. # logging.
@ -350,8 +362,7 @@ climate:
supports: supports:
mode: ["HEAT_COOL", "COOL", "HEAT", "FAN_ONLY"] mode: ["HEAT_COOL", "COOL", "HEAT", "FAN_ONLY"]
fan_mode: ["AUTO", "LOW", "MEDIUM", "HIGH"] fan_mode: ["AUTO", "LOW", "MEDIUM", "HIGH"]
swing_mode: ["OFF", "VERTICAL"] swing_mode: ["OFF", "VERTICAL", "HORIZONTAL", "BOTH"]
visual: visual:
min_temperature: 16 min_temperature: 16
max_temperature: 31 max_temperature: 31
@ -397,6 +408,7 @@ climate:
request wasn't received from your ESPHome controller. This will result request wasn't received from your ESPHome controller. This will result
in the heatpump reverting to it's internal temperature sensor if the heatpump in the heatpump reverting to it's internal temperature sensor if the heatpump
loses it's WiFi connection. loses it's WiFi connection.
equest.)
## Other configuration ## Other configuration

View File

@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv 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.components.logger import HARDWARE_UART_TO_SERIAL
from esphome.const import ( from esphome.const import (
CONF_ID, CONF_ID,
@ -15,12 +15,24 @@ from esphome.const import (
) )
from esphome.core import CORE, coroutine from esphome.core import CORE, coroutine
AUTO_LOAD = ["climate"] AUTO_LOAD = ["climate", "select"]
CONF_SUPPORTS = "supports" 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_CLIMATE_MODES = ["HEAT_COOL", "COOL", "HEAT", "DRY", "FAN_ONLY"]
DEFAULT_FAN_MODES = ["AUTO", "DIFFUSE", "LOW", "MEDIUM", "MIDDLE", "HIGH"] DEFAULT_FAN_MODES = ["AUTO", "DIFFUSE", "LOW", "MEDIUM", "MIDDLE", "HIGH"]
DEFAULT_SWING_MODES = ["OFF", "VERTICAL"] 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 # Remote temperature timeout configuration
CONF_REMOTE_OPERATING_TIMEOUT = "remote_temperature_operating_timeout_minutes" CONF_REMOTE_OPERATING_TIMEOUT = "remote_temperature_operating_timeout_minutes"
@ -31,6 +43,9 @@ MitsubishiHeatPump = cg.global_ns.class_(
"MitsubishiHeatPump", climate.Climate, cg.PollingComponent "MitsubishiHeatPump", climate.Climate, cg.PollingComponent
) )
MitsubishiACSelect = cg.global_ns.class_(
"MitsubishiACSelect", select.Select, cg.Component
)
def valid_uart(uart): def valid_uart(uart):
if CORE.is_esp8266: if CORE.is_esp8266:
@ -43,6 +58,10 @@ def valid_uart(uart):
return cv.one_of(*uarts, upper=True)(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( CONFIG_SCHEMA = climate.CLIMATE_SCHEMA.extend(
{ {
cv.GenerateID(): cv.declare_id(MitsubishiHeatPump), 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.Optional(CONF_UPDATE_INTERVAL, default="500ms"): cv.All(
cv.update_interval, cv.Range(max=cv.TimePeriod(milliseconds=9000)) 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. # Optionally override the supported ClimateTraits.
cv.Optional(CONF_SUPPORTS, default={}): cv.Schema( cv.Optional(CONF_SUPPORTS, default={}): cv.Schema(
{ {
@ -113,6 +135,18 @@ def to_code(config):
climate.CLIMATE_SWING_MODES[mode] 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 cg.register_component(var, config)
yield climate.register_climate(var, config) yield climate.register_climate(var, config)
cg.add_library( cg.add_library(

View File

@ -111,6 +111,117 @@ climate::ClimateTraits& MitsubishiHeatPump::config_traits() {
return 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. * Implement control of a MitsubishiHeatPump.
* *
@ -264,10 +375,22 @@ void MitsubishiHeatPump::control(const climate::ClimateCall &call) {
switch(*call.get_swing_mode()) { switch(*call.get_swing_mode()) {
case climate::CLIMATE_SWING_OFF: case climate::CLIMATE_SWING_OFF:
hp->setVaneSetting("AUTO"); hp->setVaneSetting("AUTO");
hp->setWideVaneSetting("|");
updated = true; updated = true;
break; break;
case climate::CLIMATE_SWING_VERTICAL: case climate::CLIMATE_SWING_VERTICAL:
hp->setVaneSetting("SWING"); 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; updated = true;
break; break;
default: default:
@ -370,15 +493,52 @@ void MitsubishiHeatPump::hpSettingsChanged() {
/* ******** HANDLE MITSUBISHI VANE CHANGES ******** /* ******** HANDLE MITSUBISHI VANE CHANGES ********
* const char* VANE_MAP[7] = {"AUTO", "1", "2", "3", "4", "5", "SWING"}; * 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; this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
} } else if (strcmp(currentSettings.wideVane, "SWING") == 0) {
else { this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
} else {
this->swing_mode = climate::CLIMATE_SWING_OFF; this->swing_mode = climate::CLIMATE_SWING_OFF;
} }
ESP_LOGI(TAG, "Swing mode is: %i", this->swing_mode); 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 ******** * ******** HANDLE TARGET TEMPERATURE CHANGES ********
@ -525,6 +685,8 @@ void MitsubishiHeatPump::setup() {
this->target_temperature = NAN; this->target_temperature = NAN;
this->fan_mode = climate::CLIMATE_FAN_OFF; this->fan_mode = climate::CLIMATE_FAN_OFF;
this->swing_mode = climate::CLIMATE_SWING_OFF; this->swing_mode = climate::CLIMATE_SWING_OFF;
this->vertical_swing_state_ = "auto";
this->horizontal_swing_state_ = "auto";
#ifdef USE_CALLBACKS #ifdef USE_CALLBACKS
hp->setSettingsChangedCallback( hp->setSettingsChangedCallback(

View File

@ -18,6 +18,7 @@
#define USE_CALLBACKS #define USE_CALLBACKS
#include "esphome.h" #include "esphome.h"
#include "esphome/components/select/select.h"
#include "esphome/core/preferences.h" #include "esphome/core/preferences.h"
#include <chrono> #include <chrono>
@ -100,6 +101,9 @@ class MitsubishiHeatPump : public esphome::PollingComponent, public esphome::cli
// set_remote_temp(0) to switch back to the internal sensor. // set_remote_temp(0) to switch back to the internal sensor.
void set_remote_temperature(float); 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 // Used to validate that a connection is present between the controller
// and this heatpump. // and this heatpump.
void ping(); void ping();
@ -123,6 +127,12 @@ class MitsubishiHeatPump : public esphome::PollingComponent, public esphome::cli
// The ClimateTraits supported by this HeatPump. // The ClimateTraits supported by this HeatPump.
esphome::climate::ClimateTraits traits_; 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_ // Allow the HeatPump class to use get_hw_serial_
friend class HeatPump; friend class HeatPump;
@ -148,6 +158,15 @@ class MitsubishiHeatPump : public esphome::PollingComponent, public esphome::cli
static void save(float value, esphome::ESPPreferenceObject& storage); static void save(float value, esphome::ESPPreferenceObject& storage);
static esphome::optional<float> load(esphome::ESPPreferenceObject& storage); static esphome::optional<float> 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); static void log_packet(byte* packet, unsigned int length, char* packetDirection);
private: private:

View File

@ -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