Files
esphome/esphome/components/mitsubishi_cn105/__init__.py
T
2026-08-21 23:52:18 -05:00

251 lines
7.1 KiB
Python

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_DIRECTION,
CONF_ID,
CONF_ON_STATE,
CONF_TEMPERATURE,
CONF_UPDATE_INTERVAL,
CONF_USE_FAHRENHEIT,
)
from esphome.core import ID, Lambda
from esphome.cpp_generator import LambdaExpression, MockObj
from esphome.types import ConfigType, TemplateArgsType
CODEOWNERS = ["@crnjan"]
DEPENDENCIES = ["uart"]
DOMAIN = "mitsubishi_cn105"
CONF_MITSUBISHI_CN105_ID = f"{DOMAIN}_id"
CONF_TELEMETRY_REQUEST_MIN_INTERVAL = "telemetry_request_min_interval"
CONF_VANE = "vane"
CONF_VERTICAL = "vertical"
mitsubishi_ns = cg.esphome_ns.namespace(DOMAIN)
MitsubishiCN105Component = mitsubishi_ns.class_(
"MitsubishiCN105Component",
cg.Component,
uart.UARTDevice,
)
VaneState = mitsubishi_ns.struct("VaneState")
VaneCall = mitsubishi_ns.class_("VaneCall")
VerticalVaneMode = mitsubishi_ns.enum("VerticalVaneMode")
# The insertion order must match VALUES in
# select/mitsubishi_cn105_vane_select_vertical.cpp.
VERTICAL_VANE_DIRECTIONS = {
"AUTO": VerticalVaneMode.VERTICAL_VANE_MODE_AUTO,
"1": VerticalVaneMode.VERTICAL_VANE_MODE_POSITION_1,
"2": VerticalVaneMode.VERTICAL_VANE_MODE_POSITION_2,
"3": VerticalVaneMode.VERTICAL_VANE_MODE_POSITION_3,
"4": VerticalVaneMode.VERTICAL_VANE_MODE_POSITION_4,
"5": VerticalVaneMode.VERTICAL_VANE_MODE_POSITION_5,
"SWING": VerticalVaneMode.VERTICAL_VANE_MODE_SWING,
}
SetRemoteTemperatureAction = mitsubishi_ns.class_(
"SetRemoteTemperatureAction",
automation.Action,
cg.Parented.template(MitsubishiCN105Component),
)
ClearRemoteTemperatureAction = mitsubishi_ns.class_(
"ClearRemoteTemperatureAction",
automation.Action,
cg.Parented.template(MitsubishiCN105Component),
)
VaneControlAction = mitsubishi_ns.class_(
"VaneControlAction",
automation.Action,
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(MitsubishiCN105Component),
cv.Optional(CONF_UPDATE_INTERVAL, default="1s"): cv.update_interval,
cv.Optional(
CONF_TELEMETRY_REQUEST_MIN_INTERVAL, default="60s"
): cv.update_interval,
cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.boolean,
cv.Optional(CONF_VANE): cv.Schema(
{
cv.Optional(CONF_ON_STATE): automation.validate_automation({}),
}
),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(uart.UART_DEVICE_SCHEMA)
)
MITSUBISHI_CN105_DEVICE_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_MITSUBISHI_CN105_ID): cv.use_id(MitsubishiCN105Component),
}
)
FINAL_VALIDATE_SCHEMA = cv.All(
uart.final_validate_device_schema(
DOMAIN,
require_rx=True,
require_tx=True,
data_bits=8,
parity="EVEN",
stop_bits=1,
)
)
async def register_mitsubishi_cn105_device(var: MockObj, config: ConfigType) -> None:
parent = await cg.get_variable(config[CONF_MITSUBISHI_CN105_ID])
cg.add(var.set_parent(parent))
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
cg.add(
var.set_telemetry_request_min_interval(
config[CONF_TELEMETRY_REQUEST_MIN_INTERVAL]
)
)
cg.add(var.set_use_fahrenheit(config[CONF_USE_FAHRENHEIT]))
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(
{
cv.Required(CONF_ID): cv.use_id(MitsubishiCN105Component),
cv.Required(CONF_TEMPERATURE): cv.templatable(
cv.All(
cv.temperature,
cv.Range(min=8.0, max=39.5),
)
),
}
)
CLEAR_REMOTE_TEMPERATURE_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(MitsubishiCN105Component),
}
)
@automation.register_action(
f"{DOMAIN}.set_remote_temperature",
SetRemoteTemperatureAction,
REMOTE_TEMPERATURE_ACTION_SCHEMA,
synchronous=True,
)
async def remote_temperature_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
temperature = await cg.templatable(config[CONF_TEMPERATURE], args, float)
cg.add(var.set_temperature(temperature))
return var
@automation.register_action(
f"{DOMAIN}.clear_remote_temperature",
ClearRemoteTemperatureAction,
CLEAR_REMOTE_TEMPERATURE_ACTION_SCHEMA,
synchronous=True,
)
async def clear_temperature_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var
VANE_CONTROL_FIELDS = (
(
(CONF_VERTICAL, CONF_DIRECTION),
"vertical.set_direction",
VerticalVaneMode,
),
)
VANE_CONTROL_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(MitsubishiCN105Component),
cv.Optional(CONF_VERTICAL): cv.Schema(
{
cv.Optional(CONF_DIRECTION): cv.templatable(
cv.enum(VERTICAL_VANE_DIRECTIONS, upper=True)
),
}
),
}
)
@automation.register_action(
f"{DOMAIN}.vane.control",
VaneControlAction,
VANE_CONTROL_ACTION_SCHEMA,
synchronous=True,
)
async def vane_control_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
cg.add_global(mitsubishi_ns.using)
parent = await cg.get_variable(config[CONF_ID])
normalized_args = [
(cg.RawExpression(f"const std::remove_cvref_t<{cg.safe_exp(t)}> &"), name)
for t, name in args
]
forwarded_args = ", ".join(name for _, name in args)
body_lines: list[str] = []
for path, setter, type_ in VANE_CONTROL_FIELDS:
if (section := config.get(path[0])) is None:
continue
if (value := section.get(path[1])) is None:
continue
if isinstance(value, Lambda):
inner = await cg.process_lambda(
value,
normalized_args,
return_type=type_,
)
body_lines.append(f"call.{setter}(({inner})({forwarded_args}));")
else:
body_lines.append(f"call.{setter}({cg.safe_exp(value)});")
apply_lambda = LambdaExpression(
["\n".join(body_lines)],
[(VaneCall.operator("ref"), "call"), *normalized_args],
capture="",
return_type=cg.void,
)
return cg.new_Pvariable(action_id, template_arg, parent, apply_lambda)