mirror of
https://github.com/esphome/esphome.git
synced 2026-02-06 01:22:47 +08:00
[ir_rf_proxy] New component (#12985)
Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
@@ -255,6 +255,7 @@ esphome/components/inkplate/* @jesserockz @JosipKuci
|
||||
esphome/components/integration/* @OttoWinter
|
||||
esphome/components/internal_temperature/* @Mat931
|
||||
esphome/components/interval/* @esphome/core
|
||||
esphome/components/ir_rf_proxy/* @kbx81
|
||||
esphome/components/jsn_sr04t/* @Mafus1
|
||||
esphome/components/json/* @esphome/core
|
||||
esphome/components/kamstrup_kmp/* @cfeenstra1024
|
||||
|
||||
11
esphome/components/ir_rf_proxy/__init__.py
Normal file
11
esphome/components/ir_rf_proxy/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
"""IR/RF Proxy component - provides remote_base backend for infrared platform."""
|
||||
|
||||
import esphome.codegen as cg
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
|
||||
# Namespace and constants exported for infrared.py platform
|
||||
ir_rf_proxy_ns = cg.esphome_ns.namespace("ir_rf_proxy")
|
||||
|
||||
CONF_REMOTE_RECEIVER_ID = "remote_receiver_id"
|
||||
CONF_REMOTE_TRANSMITTER_ID = "remote_transmitter_id"
|
||||
77
esphome/components/ir_rf_proxy/infrared.py
Normal file
77
esphome/components/ir_rf_proxy/infrared.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""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
|
||||
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_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."""
|
||||
# Only validate 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))
|
||||
23
esphome/components/ir_rf_proxy/ir_rf_proxy.cpp
Normal file
23
esphome/components/ir_rf_proxy/ir_rf_proxy.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "ir_rf_proxy.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::ir_rf_proxy {
|
||||
|
||||
static const char *const TAG = "ir_rf_proxy";
|
||||
|
||||
void IrRfProxy::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"IR/RF Proxy '%s'\n"
|
||||
" Supports Transmitter: %s\n"
|
||||
" Supports Receiver: %s",
|
||||
this->get_name().c_str(), YESNO(this->traits_.get_supports_transmitter()),
|
||||
YESNO(this->traits_.get_supports_receiver()));
|
||||
|
||||
if (this->is_rf()) {
|
||||
ESP_LOGCONFIG(TAG, " Hardware Type: RF (%.3f MHz)", this->frequency_khz_ / 1e3f);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " Hardware Type: Infrared");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::ir_rf_proxy
|
||||
32
esphome/components/ir_rf_proxy/ir_rf_proxy.h
Normal file
32
esphome/components/ir_rf_proxy/ir_rf_proxy.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
// WARNING: This component is EXPERIMENTAL. The API may change at any time
|
||||
// without following the normal breaking changes policy. Use at your own risk.
|
||||
// Once the API is considered stable, this warning will be removed.
|
||||
|
||||
#include "esphome/components/infrared/infrared.h"
|
||||
#include "esphome/components/remote_transmitter/remote_transmitter.h"
|
||||
#include "esphome/components/remote_receiver/remote_receiver.h"
|
||||
|
||||
namespace esphome::ir_rf_proxy {
|
||||
|
||||
/// IrRfProxy - Infrared platform implementation using remote_transmitter/receiver as backend
|
||||
class IrRfProxy : public infrared::Infrared {
|
||||
public:
|
||||
IrRfProxy() = default;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
/// Set RF frequency in kHz (0 = infrared, non-zero = RF)
|
||||
void set_frequency(uint32_t frequency_khz) { this->frequency_khz_ = frequency_khz; }
|
||||
/// Get RF frequency in kHz
|
||||
uint32_t get_frequency() const { return this->frequency_khz_; }
|
||||
/// Check if this is RF mode (non-zero frequency)
|
||||
bool is_rf() const { return this->frequency_khz_ > 0; }
|
||||
|
||||
protected:
|
||||
// RF frequency in kHz (Hz / 1000); 0 = infrared, non-zero = RF
|
||||
uint32_t frequency_khz_{0};
|
||||
};
|
||||
|
||||
} // namespace esphome::ir_rf_proxy
|
||||
42
tests/components/infrared/common.yaml
Normal file
42
tests/components/infrared/common.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
|
||||
api:
|
||||
|
||||
remote_transmitter:
|
||||
id: ir_transmitter
|
||||
pin: ${tx_pin}
|
||||
carrier_duty_percent: 50%
|
||||
|
||||
remote_receiver:
|
||||
id: ir_receiver
|
||||
pin: ${rx_pin}
|
||||
|
||||
# Test various hardware types with transmitter/receiver using infrared platform
|
||||
infrared:
|
||||
# Infrared transmitter
|
||||
- platform: ir_rf_proxy
|
||||
id: ir_tx
|
||||
name: "IR Transmitter"
|
||||
remote_transmitter_id: ir_transmitter
|
||||
|
||||
# Infrared receiver
|
||||
- platform: ir_rf_proxy
|
||||
id: ir_rx
|
||||
name: "IR Receiver"
|
||||
remote_receiver_id: ir_receiver
|
||||
|
||||
# RF 433MHz transmitter
|
||||
- platform: ir_rf_proxy
|
||||
id: rf_433_tx
|
||||
name: "RF 433 Transmitter"
|
||||
frequency: 433 MHz
|
||||
remote_transmitter_id: ir_transmitter
|
||||
|
||||
# RF 900MHz receiver
|
||||
- platform: ir_rf_proxy
|
||||
id: rf_900_rx
|
||||
name: "RF 900 Receiver"
|
||||
frequency: 900 MHz
|
||||
remote_receiver_id: ir_receiver
|
||||
5
tests/components/infrared/test.esp32-idf.yaml
Normal file
5
tests/components/infrared/test.esp32-idf.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
||||
5
tests/components/infrared/test.esp8266-ard.yaml
Normal file
5
tests/components/infrared/test.esp8266-ard.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
||||
5
tests/components/infrared/test.rp2040-ard.yaml
Normal file
5
tests/components/infrared/test.rp2040-ard.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
||||
42
tests/components/ir_rf_proxy/common.yaml
Normal file
42
tests/components/ir_rf_proxy/common.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
|
||||
api:
|
||||
|
||||
remote_transmitter:
|
||||
id: ir_transmitter
|
||||
pin: ${tx_pin}
|
||||
carrier_duty_percent: 50%
|
||||
|
||||
remote_receiver:
|
||||
id: ir_receiver
|
||||
pin: ${rx_pin}
|
||||
|
||||
# Test various hardware types with transmitter/receiver using infrared platform
|
||||
infrared:
|
||||
# Infrared transmitter
|
||||
- platform: ir_rf_proxy
|
||||
id: ir_tx
|
||||
name: "IR Transmitter"
|
||||
remote_transmitter_id: ir_transmitter
|
||||
|
||||
# Infrared receiver
|
||||
- platform: ir_rf_proxy
|
||||
id: ir_rx
|
||||
name: "IR Receiver"
|
||||
remote_receiver_id: ir_receiver
|
||||
|
||||
# RF 433MHz transmitter
|
||||
- platform: ir_rf_proxy
|
||||
id: rf_433_tx
|
||||
name: "RF 433 Transmitter"
|
||||
frequency: 433 MHz
|
||||
remote_transmitter_id: ir_transmitter
|
||||
|
||||
# RF 900MHz receiver
|
||||
- platform: ir_rf_proxy
|
||||
id: rf_900_rx
|
||||
name: "RF 900 Receiver"
|
||||
frequency: 900 MHz
|
||||
remote_receiver_id: ir_receiver
|
||||
5
tests/components/ir_rf_proxy/test.esp32-idf.yaml
Normal file
5
tests/components/ir_rf_proxy/test.esp32-idf.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
||||
5
tests/components/ir_rf_proxy/test.esp8266-ard.yaml
Normal file
5
tests/components/ir_rf_proxy/test.esp8266-ard.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
||||
5
tests/components/ir_rf_proxy/test.rp2040-ard.yaml
Normal file
5
tests/components/ir_rf_proxy/test.rp2040-ard.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO4
|
||||
rx_pin: GPIO5
|
||||
|
||||
<<: !include common.yaml
|
||||
Reference in New Issue
Block a user