mirror of
https://github.com/esphome/esphome.git
synced 2026-05-29 23:07:16 +08:00
[radio_frequency] Add on_control trigger; ir_rf_proxy driver-agnostic (#16368)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -106,7 +106,6 @@ void RfProxy::setup() {
|
|||||||
void RfProxy::dump_config() {
|
void RfProxy::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG,
|
ESP_LOGCONFIG(TAG,
|
||||||
"RF Proxy '%s'\n"
|
"RF Proxy '%s'\n"
|
||||||
" Backend: remote_transmitter/receiver\n"
|
|
||||||
" Supports Transmitter: %s\n"
|
" Supports Transmitter: %s\n"
|
||||||
" Supports Receiver: %s",
|
" Supports Receiver: %s",
|
||||||
this->get_name().c_str(), YESNO(this->traits_.get_supports_transmitter()),
|
this->get_name().c_str(), YESNO(this->traits_.get_supports_transmitter()),
|
||||||
@@ -124,7 +123,9 @@ void RfProxy::dump_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RfProxy::control(const radio_frequency::RadioFrequencyCall &call) {
|
void RfProxy::control(const radio_frequency::RadioFrequencyCall &call) {
|
||||||
// RF: no IR carrier modulation
|
// RF: no IR carrier modulation. Any RF front-end coordination (state turnaround, retuning)
|
||||||
|
// happens via the radio_frequency entity's on_control trigger and remote_transmitter's
|
||||||
|
// on_transmit/on_complete triggers — wired up in user YAML.
|
||||||
transmit_raw_timings(this->transmitter_, 0, call);
|
transmit_raw_timings(this->transmitter_, 0, call);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ class IrRfProxy : public infrared::Infrared {
|
|||||||
#endif // USE_IR_RF
|
#endif // USE_IR_RF
|
||||||
|
|
||||||
#ifdef USE_RADIO_FREQUENCY
|
#ifdef USE_RADIO_FREQUENCY
|
||||||
/// RfProxy - Radio Frequency platform implementation using remote_transmitter/receiver as backend
|
/// RfProxy - Radio Frequency platform implementation using remote_transmitter/receiver as backend.
|
||||||
|
/// Driver-agnostic: integration with specific RF front-end chips (CC1101, RFM69, etc.) is done
|
||||||
|
/// in YAML by wiring their actions to `remote_transmitter`'s on_transmit/on_complete triggers and
|
||||||
|
/// to this entity's on_control trigger (see radio_frequency component docs).
|
||||||
class RfProxy : public radio_frequency::RadioFrequency {
|
class RfProxy : public radio_frequency::RadioFrequency {
|
||||||
public:
|
public:
|
||||||
RfProxy() = default;
|
RfProxy() = default;
|
||||||
|
|||||||
@@ -35,17 +35,19 @@ def _final_validate(config: ConfigType) -> None:
|
|||||||
if CONF_REMOTE_TRANSMITTER_ID not in config:
|
if CONF_REMOTE_TRANSMITTER_ID not in config:
|
||||||
return
|
return
|
||||||
|
|
||||||
transmitter_id = config[CONF_REMOTE_TRANSMITTER_ID]
|
|
||||||
full_config = fv.full_config.get()
|
full_config = fv.full_config.get()
|
||||||
transmitter_path = full_config.get_path_for_id(transmitter_id)[:-1]
|
transmitter_path = full_config.get_path_for_id(config[CONF_REMOTE_TRANSMITTER_ID])[
|
||||||
|
:-1
|
||||||
|
]
|
||||||
transmitter_config = full_config.get_config_for_path(transmitter_path)
|
transmitter_config = full_config.get_config_for_path(transmitter_path)
|
||||||
|
|
||||||
duty_percent = transmitter_config.get(CONF_CARRIER_DUTY_PERCENT)
|
duty_percent = transmitter_config.get(CONF_CARRIER_DUTY_PERCENT)
|
||||||
if duty_percent is not None and duty_percent != 100:
|
if duty_percent is not None and duty_percent != 100:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
f"Transmitter '{transmitter_id}' must have '{CONF_CARRIER_DUTY_PERCENT}' "
|
f"Transmitter '{config[CONF_REMOTE_TRANSMITTER_ID]}' must have "
|
||||||
"set to 100% for RF transmission. Dedicated RF hardware handles modulation; "
|
f"'{CONF_CARRIER_DUTY_PERCENT}' set to 100% for RF transmission. "
|
||||||
"applying a carrier duty cycle would corrupt the signal"
|
"Dedicated RF hardware handles modulation; applying a carrier duty cycle "
|
||||||
|
"would corrupt the signal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ breaking changes policy. Use at your own risk.
|
|||||||
Once the API is considered stable, this warning will be removed.
|
Once the API is considered stable, this warning will be removed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from esphome import automation
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID, CONF_ON_CONTROL
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
from esphome.core.entity_helpers import queue_entity_register, setup_entity
|
from esphome.core.entity_helpers import queue_entity_register, setup_entity
|
||||||
from esphome.coroutine import CoroPriority
|
from esphome.coroutine import CoroPriority
|
||||||
@@ -42,6 +43,7 @@ def radio_frequency_schema(class_: type[cg.MockObjClass]) -> cv.Schema:
|
|||||||
return entity_schema.extend(
|
return entity_schema.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(class_),
|
cv.GenerateID(): cv.declare_id(class_),
|
||||||
|
cv.Optional(CONF_ON_CONTROL): automation.validate_automation({}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -59,6 +61,11 @@ async def register_radio_frequency(var: cg.Pvariable, config: ConfigType) -> Non
|
|||||||
await setup_radio_frequency_core_(var, config)
|
await setup_radio_frequency_core_(var, config)
|
||||||
CORE.register_platform_component("radio_frequency", var)
|
CORE.register_platform_component("radio_frequency", var)
|
||||||
|
|
||||||
|
for conf in config.get(CONF_ON_CONTROL, []):
|
||||||
|
await automation.build_callback_automation(
|
||||||
|
var, "add_on_control_callback", [(RadioFrequencyCall, "x")], conf
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def new_radio_frequency(config: ConfigType, *args) -> cg.Pvariable:
|
async def new_radio_frequency(config: ConfigType, *args) -> cg.Pvariable:
|
||||||
"""Create a new RadioFrequency instance.
|
"""Create a new RadioFrequency instance.
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ RadioFrequencyCall &RadioFrequencyCall::set_repeat_count(uint32_t count) {
|
|||||||
|
|
||||||
void RadioFrequencyCall::perform() {
|
void RadioFrequencyCall::perform() {
|
||||||
if (this->parent_ != nullptr) {
|
if (this->parent_ != nullptr) {
|
||||||
|
// Fire any on_control hooks (user-wired automations) before handing off to
|
||||||
|
// the platform-specific control() — gives users a chance to react to call
|
||||||
|
// parameters (e.g. retune an external RF front-end based on call.get_frequency()).
|
||||||
|
this->parent_->control_callback_.call(*this);
|
||||||
this->parent_->control(*this);
|
this->parent_->control(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,6 +170,15 @@ class RadioFrequency : public Component, public EntityBase, public remote_base::
|
|||||||
this->receive_callback_.add(std::forward<F>(callback));
|
this->receive_callback_.add(std::forward<F>(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a callback to invoke when a transmit call is made on this entity.
|
||||||
|
/// Fires before the platform-specific control() runs, with the call object
|
||||||
|
/// (containing frequency, modulation, repeat count, etc.). Used by the
|
||||||
|
/// `on_control` YAML trigger so users can wire any RF front-end driver
|
||||||
|
/// (CC1101, RFM69, custom) to react to per-call parameters.
|
||||||
|
template<typename F> void add_on_control_callback(F &&callback) {
|
||||||
|
this->control_callback_.add(std::forward<F>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class RadioFrequencyCall;
|
friend class RadioFrequencyCall;
|
||||||
|
|
||||||
@@ -182,6 +191,8 @@ class RadioFrequency : public Component, public EntityBase, public remote_base::
|
|||||||
|
|
||||||
// Callback manager for receive events (lazy: saves memory when no callbacks registered)
|
// Callback manager for receive events (lazy: saves memory when no callbacks registered)
|
||||||
LazyCallbackManager<void(remote_base::RemoteReceiveData)> receive_callback_;
|
LazyCallbackManager<void(remote_base::RemoteReceiveData)> receive_callback_;
|
||||||
|
// Callback manager for on_control trigger (lazy: same memory savings)
|
||||||
|
LazyCallbackManager<void(const RadioFrequencyCall &)> control_callback_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esphome::radio_frequency
|
} // namespace esphome::radio_frequency
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
cc1101:
|
||||||
|
id: cc1101_radio
|
||||||
|
cs_pin: ${cs_pin}
|
||||||
|
frequency: 433.92MHz
|
||||||
|
modulation_type: ASK/OOK
|
||||||
|
output_power: 10
|
||||||
|
|
||||||
|
# Dual-pin wiring (recommended by the CC1101 docs):
|
||||||
|
# CC1101 GDO0 → ${gdo0_pin} (remote_transmitter)
|
||||||
|
# CC1101 GDO2 → ${gdo2_pin} (remote_receiver)
|
||||||
|
remote_transmitter:
|
||||||
|
id: rf_tx
|
||||||
|
pin: ${gdo0_pin}
|
||||||
|
carrier_duty_percent: 100%
|
||||||
|
# Switch the chip into TX state for the duration of each transmission and back to RX
|
||||||
|
# afterwards. Driver-agnostic: any RF front-end with begin_tx/begin_rx-style actions
|
||||||
|
# can be wired this way.
|
||||||
|
on_transmit:
|
||||||
|
then:
|
||||||
|
- cc1101.begin_tx: cc1101_radio
|
||||||
|
on_complete:
|
||||||
|
then:
|
||||||
|
- cc1101.begin_rx: cc1101_radio
|
||||||
|
|
||||||
|
remote_receiver:
|
||||||
|
id: rf_rx
|
||||||
|
pin: ${gdo2_pin}
|
||||||
|
|
||||||
|
radio_frequency:
|
||||||
|
- platform: ir_rf_proxy
|
||||||
|
id: rf_proxy_cc1101_tx
|
||||||
|
name: "CC1101 RF Transmitter"
|
||||||
|
frequency: 433.92MHz
|
||||||
|
remote_transmitter_id: rf_tx
|
||||||
|
# Optional: retune the CC1101 per-transmit when the API request specifies a
|
||||||
|
# different carrier frequency. Demonstrates the on_control trigger.
|
||||||
|
on_control:
|
||||||
|
then:
|
||||||
|
- if:
|
||||||
|
condition:
|
||||||
|
lambda: "return x.get_frequency().has_value() && *x.get_frequency() > 0;"
|
||||||
|
then:
|
||||||
|
- cc1101.set_frequency:
|
||||||
|
id: cc1101_radio
|
||||||
|
value: !lambda "return *x.get_frequency();"
|
||||||
|
- platform: ir_rf_proxy
|
||||||
|
id: rf_proxy_cc1101_rx
|
||||||
|
name: "CC1101 RF Receiver"
|
||||||
|
frequency: 433.92MHz
|
||||||
|
remote_receiver_id: rf_rx
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
substitutions:
|
||||||
|
cs_pin: GPIO5
|
||||||
|
gdo0_pin: GPIO4
|
||||||
|
gdo2_pin: GPIO16
|
||||||
|
|
||||||
|
packages:
|
||||||
|
common: !include common.yaml
|
||||||
|
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
|
||||||
|
cc1101: !include common-cc1101.yaml
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
substitutions:
|
||||||
|
cs_pin: GPIO5
|
||||||
|
gdo0_pin: GPIO4
|
||||||
|
gdo2_pin: GPIO16
|
||||||
|
|
||||||
|
packages:
|
||||||
|
common: !include common.yaml
|
||||||
|
spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml
|
||||||
|
cc1101: !include common-cc1101.yaml
|
||||||
Reference in New Issue
Block a user