mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 14:19:03 +00:00
[mitsubishi_cn105] Add basic swing support (#15653)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,8 +1,14 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import climate, uart
|
||||
from esphome.components.climate import validate_climate_swing_mode
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_SUPPORTED_SWING_MODES,
|
||||
CONF_TEMPERATURE,
|
||||
CONF_UPDATE_INTERVAL,
|
||||
)
|
||||
from esphome.core import ID
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.types import ConfigType, TemplateArgsType
|
||||
@@ -43,6 +49,9 @@ CONFIG_SCHEMA = (
|
||||
cv.Optional(
|
||||
CONF_CURRENT_TEMPERATURE_MIN_INTERVAL, default="60s"
|
||||
): cv.update_interval,
|
||||
cv.Optional(
|
||||
CONF_SUPPORTED_SWING_MODES, default="OFF"
|
||||
): validate_climate_swing_mode,
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -63,6 +72,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
var = await climate.new_climate(config)
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
cg.add(var.set_supported_swing_mode(config[CONF_SUPPORTED_SWING_MODES]))
|
||||
cg.add(
|
||||
var.set_current_temperature_min_interval(
|
||||
config[CONF_CURRENT_TEMPERATURE_MIN_INTERVAL]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
#include "esphome/components/uart/uart.h"
|
||||
#include "esphome/core/finite_set_mask.h"
|
||||
|
||||
@@ -84,6 +84,8 @@ climate::ClimateTraits MitsubishiCN105Climate::traits() {
|
||||
traits.add_supported_fan_mode(p.second);
|
||||
}
|
||||
|
||||
traits.set_supported_swing_modes(this->supported_swing_modes_);
|
||||
|
||||
traits.set_visual_min_temperature(16.0f);
|
||||
traits.set_visual_max_temperature(31.0f);
|
||||
traits.set_visual_temperature_step(1.0f);
|
||||
@@ -114,6 +116,37 @@ void MitsubishiCN105Climate::control(const climate::ClimateCall &call) {
|
||||
this->hp_.set_fan_mode(*fan_mode);
|
||||
}
|
||||
|
||||
if (const auto swing_mode = call.get_swing_mode()) {
|
||||
auto vane = this->last_non_swing_vane_mode_;
|
||||
auto wide = this->last_non_swing_wide_vane_mode_;
|
||||
|
||||
switch (*swing_mode) {
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
vane = MitsubishiCN105::VaneMode::SWING;
|
||||
wide = MitsubishiCN105::WideVaneMode::SWING;
|
||||
break;
|
||||
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
vane = MitsubishiCN105::VaneMode::SWING;
|
||||
break;
|
||||
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
wide = MitsubishiCN105::WideVaneMode::SWING;
|
||||
break;
|
||||
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->supported_swing_modes_.count(climate::CLIMATE_SWING_VERTICAL)) {
|
||||
this->hp_.set_vane_mode(vane);
|
||||
}
|
||||
if (this->supported_swing_modes_.count(climate::CLIMATE_SWING_HORIZONTAL)) {
|
||||
this->hp_.set_wide_vane_mode(wide);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->hp_.is_status_initialized()) {
|
||||
this->apply_values_();
|
||||
}
|
||||
@@ -143,7 +176,64 @@ void MitsubishiCN105Climate::apply_values_() {
|
||||
ESP_LOGD(TAG, "Unable to map fan mode");
|
||||
}
|
||||
|
||||
if (!this->supported_swing_modes_.empty()) {
|
||||
bool vertical_swinging = false;
|
||||
bool horizontal_swinging = false;
|
||||
|
||||
if (this->supported_swing_modes_.count(climate::CLIMATE_SWING_VERTICAL)) {
|
||||
if (status.vane_mode == MitsubishiCN105::VaneMode::SWING) {
|
||||
vertical_swinging = true;
|
||||
} else if (status.vane_mode != MitsubishiCN105::VaneMode::UNKNOWN) {
|
||||
this->last_non_swing_vane_mode_ = status.vane_mode;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->supported_swing_modes_.count(climate::CLIMATE_SWING_HORIZONTAL)) {
|
||||
if (status.wide_vane_mode == MitsubishiCN105::WideVaneMode::SWING) {
|
||||
horizontal_swinging = true;
|
||||
} else if (status.wide_vane_mode != MitsubishiCN105::WideVaneMode::UNKNOWN) {
|
||||
this->last_non_swing_wide_vane_mode_ = status.wide_vane_mode;
|
||||
}
|
||||
}
|
||||
|
||||
if (vertical_swinging && horizontal_swinging) {
|
||||
this->swing_mode = climate::CLIMATE_SWING_BOTH;
|
||||
} else if (vertical_swinging) {
|
||||
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||
} else if (horizontal_swinging) {
|
||||
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
|
||||
} else {
|
||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
void MitsubishiCN105Climate::set_supported_swing_mode(climate::ClimateSwingMode mode) {
|
||||
this->supported_swing_modes_.clear();
|
||||
switch (mode) {
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
this->supported_swing_modes_.insert(climate::CLIMATE_SWING_OFF);
|
||||
this->supported_swing_modes_.insert(climate::CLIMATE_SWING_VERTICAL);
|
||||
break;
|
||||
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
this->supported_swing_modes_.insert(climate::CLIMATE_SWING_OFF);
|
||||
this->supported_swing_modes_.insert(climate::CLIMATE_SWING_HORIZONTAL);
|
||||
break;
|
||||
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
this->supported_swing_modes_.insert(climate::CLIMATE_SWING_OFF);
|
||||
this->supported_swing_modes_.insert(climate::CLIMATE_SWING_VERTICAL);
|
||||
this->supported_swing_modes_.insert(climate::CLIMATE_SWING_HORIZONTAL);
|
||||
this->supported_swing_modes_.insert(climate::CLIMATE_SWING_BOTH);
|
||||
break;
|
||||
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::mitsubishi_cn105
|
||||
|
||||
@@ -25,10 +25,15 @@ class MitsubishiCN105Climate : public climate::Climate, public Component, public
|
||||
void set_remote_temperature(float temperature) { this->hp_.set_remote_temperature(temperature); }
|
||||
void clear_remote_temperature() { this->hp_.clear_remote_temperature(); }
|
||||
|
||||
void set_supported_swing_mode(climate::ClimateSwingMode mode);
|
||||
|
||||
protected:
|
||||
void apply_values_();
|
||||
|
||||
MitsubishiCN105 hp_;
|
||||
climate::ClimateSwingModeMask supported_swing_modes_{};
|
||||
MitsubishiCN105::VaneMode last_non_swing_vane_mode_{MitsubishiCN105::VaneMode::AUTO};
|
||||
MitsubishiCN105::WideVaneMode last_non_swing_wide_vane_mode_{MitsubishiCN105::WideVaneMode::CENTER};
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
#include "../common.h"
|
||||
|
||||
namespace esphome::mitsubishi_cn105::testing {
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, SupportedSwingModeOffLeavesTraitsEmpty) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_OFF);
|
||||
|
||||
EXPECT_FALSE(sut.traits().get_supports_swing_modes());
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, SupportedSwingModeVerticalExposesOffAndVertical) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
|
||||
|
||||
EXPECT_TRUE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_OFF));
|
||||
EXPECT_TRUE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_VERTICAL));
|
||||
EXPECT_FALSE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_HORIZONTAL));
|
||||
EXPECT_FALSE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_BOTH));
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, SupportedSwingModeHorizontalExposesOffAndHorizontal) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_HORIZONTAL);
|
||||
|
||||
EXPECT_TRUE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_OFF));
|
||||
EXPECT_FALSE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_VERTICAL));
|
||||
EXPECT_TRUE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_HORIZONTAL));
|
||||
EXPECT_FALSE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_BOTH));
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, SupportedSwingModeBothExposesAllExpectedModes) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
|
||||
|
||||
EXPECT_TRUE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_OFF));
|
||||
EXPECT_TRUE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_VERTICAL));
|
||||
EXPECT_TRUE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_HORIZONTAL));
|
||||
EXPECT_TRUE(sut.traits().supports_swing_mode(climate::CLIMATE_SWING_BOTH));
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, ApplyValuesMapsVerticalSwingWhenSupported) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
|
||||
|
||||
sut.status().vane_mode = MitsubishiCN105::VaneMode::SWING;
|
||||
sut.status().wide_vane_mode = MitsubishiCN105::WideVaneMode::CENTER;
|
||||
|
||||
sut.apply_values_();
|
||||
|
||||
EXPECT_EQ(sut.swing_mode, climate::CLIMATE_SWING_VERTICAL);
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, ApplyValuesMapsHorizontalSwingWhenSupported) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_HORIZONTAL);
|
||||
|
||||
sut.status().vane_mode = MitsubishiCN105::VaneMode::AUTO;
|
||||
sut.status().wide_vane_mode = MitsubishiCN105::WideVaneMode::SWING;
|
||||
|
||||
sut.apply_values_();
|
||||
|
||||
EXPECT_EQ(sut.swing_mode, climate::CLIMATE_SWING_HORIZONTAL);
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, ApplyValuesMapsBothSwingWhenSupported) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
|
||||
|
||||
sut.status().vane_mode = MitsubishiCN105::VaneMode::SWING;
|
||||
sut.status().wide_vane_mode = MitsubishiCN105::WideVaneMode::SWING;
|
||||
|
||||
sut.apply_values_();
|
||||
|
||||
EXPECT_EQ(sut.swing_mode, climate::CLIMATE_SWING_BOTH);
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, ApplyValuesMapsSwingOffWhenNoSwingActive) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
|
||||
|
||||
sut.status().vane_mode = MitsubishiCN105::VaneMode::POSITION_3;
|
||||
sut.status().wide_vane_mode = MitsubishiCN105::WideVaneMode::CENTER;
|
||||
|
||||
sut.apply_values_();
|
||||
|
||||
EXPECT_EQ(sut.swing_mode, climate::CLIMATE_SWING_OFF);
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, ApplyValuesRemembersLastNonSwingPositions) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
|
||||
|
||||
sut.status().vane_mode = MitsubishiCN105::VaneMode::POSITION_4;
|
||||
sut.status().wide_vane_mode = MitsubishiCN105::WideVaneMode::RIGHT;
|
||||
|
||||
sut.apply_values_();
|
||||
|
||||
EXPECT_EQ(sut.last_non_swing_vane_mode_, MitsubishiCN105::VaneMode::POSITION_4);
|
||||
EXPECT_EQ(sut.last_non_swing_wide_vane_mode_, MitsubishiCN105::WideVaneMode::RIGHT);
|
||||
|
||||
sut.status().vane_mode = MitsubishiCN105::VaneMode::SWING;
|
||||
sut.status().wide_vane_mode = MitsubishiCN105::WideVaneMode::SWING;
|
||||
|
||||
sut.apply_values_();
|
||||
|
||||
EXPECT_EQ(sut.last_non_swing_vane_mode_, MitsubishiCN105::VaneMode::POSITION_4);
|
||||
EXPECT_EQ(sut.last_non_swing_wide_vane_mode_, MitsubishiCN105::WideVaneMode::RIGHT);
|
||||
EXPECT_EQ(sut.swing_mode, climate::CLIMATE_SWING_BOTH);
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, ApplyValuesDoesNotOverwriteRememberedPositionWithUnknownValues) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_BOTH);
|
||||
|
||||
sut.last_non_swing_vane_mode_ = MitsubishiCN105::VaneMode::POSITION_2;
|
||||
sut.last_non_swing_wide_vane_mode_ = MitsubishiCN105::WideVaneMode::LEFT;
|
||||
|
||||
sut.status().vane_mode = MitsubishiCN105::VaneMode::UNKNOWN;
|
||||
sut.status().wide_vane_mode = MitsubishiCN105::WideVaneMode::UNKNOWN;
|
||||
|
||||
sut.apply_values_();
|
||||
|
||||
EXPECT_EQ(sut.last_non_swing_vane_mode_, MitsubishiCN105::VaneMode::POSITION_2);
|
||||
EXPECT_EQ(sut.last_non_swing_wide_vane_mode_, MitsubishiCN105::WideVaneMode::LEFT);
|
||||
EXPECT_EQ(sut.swing_mode, climate::CLIMATE_SWING_OFF);
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, ApplyValuesIgnoresUnsupportedVerticalSwingState) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_HORIZONTAL);
|
||||
|
||||
sut.status().vane_mode = MitsubishiCN105::VaneMode::SWING;
|
||||
sut.status().wide_vane_mode = MitsubishiCN105::WideVaneMode::CENTER;
|
||||
|
||||
sut.apply_values_();
|
||||
|
||||
EXPECT_EQ(sut.swing_mode, climate::CLIMATE_SWING_OFF);
|
||||
}
|
||||
|
||||
TEST(MitsubishiCN105ClimateTests, ApplyValuesIgnoresUnsupportedHorizontalSwingState) {
|
||||
TestableMitsubishiCN105Climate sut;
|
||||
|
||||
sut.set_supported_swing_mode(climate::CLIMATE_SWING_VERTICAL);
|
||||
|
||||
sut.status().vane_mode = MitsubishiCN105::VaneMode::AUTO;
|
||||
sut.status().wide_vane_mode = MitsubishiCN105::WideVaneMode::SWING;
|
||||
|
||||
sut.apply_values_();
|
||||
|
||||
EXPECT_EQ(sut.swing_mode, climate::CLIMATE_SWING_OFF);
|
||||
}
|
||||
|
||||
} // namespace esphome::mitsubishi_cn105::testing
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
#include "esphome/components/uart/uart_component.h"
|
||||
#include "esphome/components/mitsubishi_cn105/mitsubishi_cn105.h"
|
||||
#include "esphome/components/mitsubishi_cn105/mitsubishi_cn105_climate.h"
|
||||
|
||||
namespace esphome::mitsubishi_cn105::testing {
|
||||
|
||||
@@ -44,6 +45,7 @@ class TestableMitsubishiCN105 : public MitsubishiCN105 {
|
||||
using MitsubishiCN105::State;
|
||||
using MitsubishiCN105::UpdateFlag;
|
||||
using MitsubishiCN105::state_;
|
||||
using MitsubishiCN105::status_;
|
||||
using MitsubishiCN105::operation_start_ms_;
|
||||
using MitsubishiCN105::use_temperature_encoding_b_;
|
||||
using MitsubishiCN105::set_wide_vane_high_bit_;
|
||||
@@ -58,4 +60,13 @@ class TestableMitsubishiCN105 : public MitsubishiCN105 {
|
||||
void set_current_time(uint32_t ms) { test_loop_time_ms = ms; }
|
||||
};
|
||||
|
||||
class TestableMitsubishiCN105Climate : public MitsubishiCN105Climate {
|
||||
public:
|
||||
using MitsubishiCN105Climate::apply_values_;
|
||||
using MitsubishiCN105Climate::last_non_swing_vane_mode_;
|
||||
using MitsubishiCN105Climate::last_non_swing_wide_vane_mode_;
|
||||
|
||||
MitsubishiCN105::Status &status() { return static_cast<TestableMitsubishiCN105 &>(this->hp_).status_; }
|
||||
};
|
||||
|
||||
} // namespace esphome::mitsubishi_cn105::testing
|
||||
|
||||
@@ -3,6 +3,9 @@ climate:
|
||||
id: ac
|
||||
name: "AC Test"
|
||||
uart_id: uart_bus
|
||||
update_interval: 30s
|
||||
current_temperature_min_interval: 120s
|
||||
supported_swing_modes: BOTH
|
||||
|
||||
esphome:
|
||||
on_boot:
|
||||
|
||||
Reference in New Issue
Block a user