"""Infrared platform implementation using remote_base (remote_transmitter/receiver).""" from typing import Any import esphome.codegen as cg from esphome.components import infrared, remote_receiver, remote_transmitter from esphome.components.const import CONF_RECEIVER_FREQUENCY import esphome.config_validation as cv from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_FREQUENCY import esphome.final_validate as fv from . import CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID, ir_rf_proxy_ns CODEOWNERS = ["@kbx81"] DEPENDENCIES = ["infrared"] IrRfProxy = ir_rf_proxy_ns.class_("IrRfProxy", infrared.Infrared) CONFIG_SCHEMA = cv.All( infrared.infrared_schema(IrRfProxy).extend( { cv.Optional(CONF_FREQUENCY, default=0): cv.frequency, cv.Optional(CONF_RECEIVER_FREQUENCY): cv.frequency, cv.Optional(CONF_REMOTE_RECEIVER_ID): cv.use_id( remote_receiver.RemoteReceiverComponent ), cv.Optional(CONF_REMOTE_TRANSMITTER_ID): cv.use_id( remote_transmitter.RemoteTransmitterComponent ), } ), cv.has_exactly_one_key(CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID), ) def _final_validate(config: dict[str, Any]) -> None: """Validate that transmitters have a proper carrier duty cycle.""" # receiver_frequency is only meaningful for receiver configurations if CONF_RECEIVER_FREQUENCY in config and CONF_REMOTE_RECEIVER_ID not in config: raise cv.Invalid( f"'{CONF_RECEIVER_FREQUENCY}' can only be used with '{CONF_REMOTE_RECEIVER_ID}', " "not with a transmitter" ) # Only validate duty cycle if this is an infrared (not RF) configuration with a transmitter if config.get(CONF_FREQUENCY, 0) != 0 or CONF_REMOTE_TRANSMITTER_ID not in config: return # Get the transmitter configuration transmitter_id = config[CONF_REMOTE_TRANSMITTER_ID] full_config = fv.full_config.get() transmitter_path = full_config.get_path_for_id(transmitter_id)[:-1] transmitter_config = full_config.get_config_for_path(transmitter_path) # Check if carrier_duty_percent set to 0 or 100 # Note: remote_transmitter schema requires this field and validates 1-100%, # but we double-check here for infrared to provide a helpful error message duty_percent = transmitter_config.get(CONF_CARRIER_DUTY_PERCENT) if duty_percent in {0, 100}: raise cv.Invalid( f"Transmitter '{transmitter_id}' must have '{CONF_CARRIER_DUTY_PERCENT}' configured with " "an intermediate value (typically 30-50%) for infrared transmission. If this is an RF " f"transmitter, configure this infrared with a '{CONF_FREQUENCY}' value greater than 0" ) FINAL_VALIDATE_SCHEMA = _final_validate async def to_code(config: dict[str, Any]) -> None: """Code generation for remote_base infrared platform.""" # Create and register the infrared entity var = await infrared.new_infrared(config) # Set frequency / 1000; zero indicates infrared hardware cg.add(var.set_frequency(config[CONF_FREQUENCY] / 1000)) # Link transmitter if specified if CONF_REMOTE_TRANSMITTER_ID in config: transmitter = await cg.get_variable(config[CONF_REMOTE_TRANSMITTER_ID]) cg.add(var.set_transmitter(transmitter)) # Link receiver if specified if CONF_REMOTE_RECEIVER_ID in config: receiver = await cg.get_variable(config[CONF_REMOTE_RECEIVER_ID]) cg.add(var.set_receiver(receiver)) # Set receiver demodulation frequency if specified (metadata only, no hardware effect) if CONF_RECEIVER_FREQUENCY in config: cg.add(var.set_receiver_frequency(config[CONF_RECEIVER_FREQUENCY]))