[mitsubishi_cn105] Add vertical vane state trigger (#16727)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Boris Krivonog
2026-08-11 13:56:12 -05:00
committed by GitHub
co-authored by J. Nick Koston
parent 3ae651af7b
commit 55bd63732d
7 changed files with 112 additions and 10 deletions
@@ -2,7 +2,7 @@ from esphome import automation
import esphome.codegen as cg
from esphome.components import uart
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TEMPERATURE, CONF_UPDATE_INTERVAL
from esphome.core import ID
from esphome.cpp_generator import MockObj
from esphome.types import ConfigType, TemplateArgsType
@@ -13,6 +13,7 @@ DOMAIN = "mitsubishi_cn105"
CONF_MITSUBISHI_CN105_ID = f"{DOMAIN}_id"
CONF_TELEMETRY_REQUEST_MIN_INTERVAL = "telemetry_request_min_interval"
CONF_VANE = "vane"
mitsubishi_ns = cg.esphome_ns.namespace(DOMAIN)
@@ -22,6 +23,8 @@ MitsubishiCN105Component = mitsubishi_ns.class_(
uart.UARTDevice,
)
VaneState = mitsubishi_ns.struct("VaneState")
SetRemoteTemperatureAction = mitsubishi_ns.class_(
"SetRemoteTemperatureAction",
automation.Action,
@@ -42,6 +45,11 @@ CONFIG_SCHEMA = (
cv.Optional(
CONF_TELEMETRY_REQUEST_MIN_INTERVAL, default="60s"
): cv.update_interval,
cv.Optional(CONF_VANE): cv.Schema(
{
cv.Optional(CONF_ON_STATE): automation.validate_automation({}),
}
),
}
)
.extend(cv.COMPONENT_SCHEMA)
@@ -80,6 +88,15 @@ async def to_code(config: ConfigType) -> None:
config[CONF_TELEMETRY_REQUEST_MIN_INTERVAL]
)
)
if on_state := config.get(CONF_VANE, {}).get(CONF_ON_STATE):
cg.add_global(mitsubishi_ns.using)
for conf in on_state:
await automation.build_callback_automation(
var,
"add_on_vane_state_callback",
[(VaneState.operator("const").operator("ref"), "x")],
conf,
)
REMOTE_TEMPERATURE_ACTION_SCHEMA = cv.Schema(
@@ -27,7 +27,7 @@ void MitsubishiCN105Component::setup() { this->hp_.initialize(); }
void MitsubishiCN105Component::loop() {
if (this->hp_.update()) {
this->status_callback_.call();
this->notify_status_listeners_();
}
}
@@ -9,6 +9,24 @@
namespace esphome::mitsubishi_cn105 {
enum VerticalVaneMode : uint8_t {
VERTICAL_VANE_MODE_AUTO = static_cast<uint8_t>(MitsubishiCN105::VaneMode::AUTO),
VERTICAL_VANE_MODE_POSITION_1 = static_cast<uint8_t>(MitsubishiCN105::VaneMode::POSITION_1),
VERTICAL_VANE_MODE_POSITION_2 = static_cast<uint8_t>(MitsubishiCN105::VaneMode::POSITION_2),
VERTICAL_VANE_MODE_POSITION_3 = static_cast<uint8_t>(MitsubishiCN105::VaneMode::POSITION_3),
VERTICAL_VANE_MODE_POSITION_4 = static_cast<uint8_t>(MitsubishiCN105::VaneMode::POSITION_4),
VERTICAL_VANE_MODE_POSITION_5 = static_cast<uint8_t>(MitsubishiCN105::VaneMode::POSITION_5),
VERTICAL_VANE_MODE_SWING = static_cast<uint8_t>(MitsubishiCN105::VaneMode::SWING),
};
struct VaneState {
struct Vertical {
VerticalVaneMode direction;
};
Vertical vertical;
};
class MitsubishiCN105Component : public Component, public uart::UARTDevice {
public:
explicit MitsubishiCN105Component() : hp_(*this) {}
@@ -38,15 +56,29 @@ class MitsubishiCN105Component : public Component, public uart::UARTDevice {
this->status_callback_.add(std::forward<F>(callback));
}
template<typename F> void add_on_vane_state_callback(F &&callback) {
this->vane_state_callback_.add(std::forward<F>(callback));
}
void publish_status() {
if (this->is_status_initialized()) {
this->status_callback_.call();
this->notify_status_listeners_();
}
}
protected:
void notify_status_listeners_() {
this->status_callback_.call();
if (this->status().vane_mode != MitsubishiCN105::VaneMode::UNKNOWN) {
this->vane_state_callback_.call(VaneState{
.vertical = {.direction = static_cast<VerticalVaneMode>(this->status().vane_mode)},
});
}
}
MitsubishiCN105 hp_;
CallbackManager<void()> status_callback_;
LazyCallbackManager<void(const VaneState &)> vane_state_callback_;
};
} // namespace esphome::mitsubishi_cn105
@@ -77,4 +77,11 @@ class TestableMitsubishiCN105Climate : public MitsubishiCN105Climate {
MitsubishiCN105Component component_;
};
class TestableMitsubishiCN105Component : public MitsubishiCN105Component {
public:
MitsubishiCN105::Status &mutable_status() { return const_cast<MitsubishiCN105::Status &>(this->status()); }
void notify_status() { this->status_callback_.call(); }
};
} // namespace esphome::mitsubishi_cn105::testing
@@ -3,6 +3,11 @@ mitsubishi_cn105:
uart_id: uart_bus
update_interval: 30s
telemetry_request_min_interval: 120s
vane:
on_state:
- logger.log:
format: "TRIGGER: vane on_state is auto: %s"
args: ['x.vertical.direction == VERTICAL_VANE_MODE_AUTO ? "yes" : "no"']
climate:
- platform: mitsubishi_cn105
@@ -0,0 +1,48 @@
#include "common.h"
namespace esphome::mitsubishi_cn105::testing {
TEST(MitsubishiCN105ComponentTests, PublishesVaneStateForEveryValidSnapshot) {
TestableMitsubishiCN105Component hub;
size_t callback_count = 0;
std::optional<VerticalVaneMode> callback_direction;
hub.add_on_vane_state_callback([&](const VaneState &state) {
callback_count++;
callback_direction = state.vertical.direction;
});
hub.mutable_status().room_temperature = 20.0f;
hub.mutable_status().vane_mode = MitsubishiCN105::VaneMode::POSITION_4;
hub.publish_status();
EXPECT_EQ(callback_count, 1);
EXPECT_EQ(callback_direction, std::optional{VERTICAL_VANE_MODE_POSITION_4});
hub.publish_status();
EXPECT_EQ(callback_count, 2);
EXPECT_EQ(callback_direction, std::optional{VERTICAL_VANE_MODE_POSITION_4});
}
TEST(MitsubishiCN105ComponentTests, DoesNotPublishUnknownVaneState) {
TestableMitsubishiCN105Component hub;
size_t status_callback_count = 0;
size_t vane_callback_count = 0;
hub.add_on_status_callback([&]() { status_callback_count++; });
hub.add_on_vane_state_callback([&](const VaneState &) { vane_callback_count++; });
hub.mutable_status().room_temperature = 20.0f;
hub.mutable_status().vane_mode = MitsubishiCN105::VaneMode::UNKNOWN;
hub.publish_status();
EXPECT_EQ(status_callback_count, 1);
EXPECT_EQ(vane_callback_count, 0);
hub.mutable_status().vane_mode = MitsubishiCN105::VaneMode::POSITION_4;
hub.publish_status();
EXPECT_EQ(status_callback_count, 2);
EXPECT_EQ(vane_callback_count, 1);
}
} // namespace esphome::mitsubishi_cn105::testing
@@ -3,13 +3,6 @@
namespace esphome::mitsubishi_cn105::testing {
class TestableMitsubishiCN105Component : public MitsubishiCN105Component {
public:
MitsubishiCN105::Status &mutable_status() { return const_cast<MitsubishiCN105::Status &>(this->status()); }
void notify_status() { this->status_callback_.call(); }
};
class TestableMitsubishiCN105VerticalVaneDirectionSelect : public MitsubishiCN105VerticalVaneDirectionSelect {
public:
using MitsubishiCN105VerticalVaneDirectionSelect::control;