mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 23:37:34 +00:00
[remote_base] Size the receiver lists by the busiest receiver and hoist attach_receiver
This commit is contained in:
@@ -119,7 +119,8 @@ class Infrared : public Component, public EntityBase, public remote_base::Remote
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::AFTER_CONNECTION; }
|
||||
|
||||
/// Set the remote receiver component
|
||||
/// Set the remote receiver component; the listener registration happens from codegen, see
|
||||
/// remote_base.attach_receiver
|
||||
void set_receiver(remote_base::RemoteReceiverBase *receiver) { this->receiver_ = receiver; }
|
||||
/// Set the remote transmitter component
|
||||
void set_transmitter(remote_base::RemoteTransmitterBase *transmitter) { this->transmitter_ = transmitter; }
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
"""IR/RF Proxy component - provides remote_base backend for infrared platform."""
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import remote_base
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.types import ConfigType
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
|
||||
@@ -12,10 +9,3 @@ 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"
|
||||
|
||||
|
||||
async def attach_receiver(var: MockObj, config: ConfigType) -> None:
|
||||
"""Wire the configured remote_receiver to a proxy entity and register it as a listener."""
|
||||
receiver = await cg.get_variable(config[CONF_REMOTE_RECEIVER_ID])
|
||||
cg.add(var.set_receiver(receiver))
|
||||
remote_base.add_listener(receiver, var)
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
from typing import Any
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import infrared, remote_receiver, remote_transmitter
|
||||
from esphome.components import (
|
||||
infrared,
|
||||
remote_base,
|
||||
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,
|
||||
attach_receiver,
|
||||
ir_rf_proxy_ns,
|
||||
)
|
||||
from . import CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID, ir_rf_proxy_ns
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
DEPENDENCIES = ["infrared"]
|
||||
@@ -87,7 +87,7 @@ async def to_code(config: dict[str, Any]) -> None:
|
||||
|
||||
# Link receiver if specified
|
||||
if CONF_REMOTE_RECEIVER_ID in config:
|
||||
await attach_receiver(var, config)
|
||||
await remote_base.attach_receiver(var, config, CONF_REMOTE_RECEIVER_ID)
|
||||
|
||||
# Set receiver demodulation frequency if specified (metadata only, no hardware effect)
|
||||
if CONF_RECEIVER_FREQUENCY in config:
|
||||
|
||||
@@ -56,7 +56,8 @@ class RfProxy final : public radio_frequency::RadioFrequency {
|
||||
|
||||
/// Set the remote transmitter component
|
||||
void set_transmitter(remote_base::RemoteTransmitterBase *transmitter) { this->transmitter_ = transmitter; }
|
||||
/// Set the remote receiver component
|
||||
/// Set the remote receiver component; the listener registration happens from codegen, see
|
||||
/// remote_base.attach_receiver
|
||||
void set_receiver(remote_base::RemoteReceiverBase *receiver) { this->receiver_ = receiver; }
|
||||
|
||||
/// Set the fixed carrier frequency in Hz (metadata: advertised via traits, does not tune hardware)
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
"""Radio Frequency platform implementation using remote_base (remote_transmitter/receiver)."""
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import radio_frequency, remote_receiver, remote_transmitter
|
||||
from esphome.components import (
|
||||
radio_frequency,
|
||||
remote_base,
|
||||
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 esphome.types import ConfigType
|
||||
|
||||
from . import (
|
||||
CONF_REMOTE_RECEIVER_ID,
|
||||
CONF_REMOTE_TRANSMITTER_ID,
|
||||
attach_receiver,
|
||||
ir_rf_proxy_ns,
|
||||
)
|
||||
from . import CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID, ir_rf_proxy_ns
|
||||
|
||||
CODEOWNERS = ["@kbx81"]
|
||||
DEPENDENCIES = ["radio_frequency"]
|
||||
@@ -71,4 +71,4 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_transmitter(transmitter))
|
||||
|
||||
if CONF_REMOTE_RECEIVER_ID in config:
|
||||
await attach_receiver(var, config)
|
||||
await remote_base.attach_receiver(var, config, CONF_REMOTE_RECEIVER_ID)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -44,7 +45,8 @@ from esphome.const import (
|
||||
CONF_WAND_ID,
|
||||
CONF_ZERO,
|
||||
)
|
||||
from esphome.core import ID, coroutine
|
||||
from esphome.core import CORE, ID, coroutine, coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
from esphome.cpp_generator import MockObj
|
||||
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||
from esphome.types import ConfigType
|
||||
@@ -52,6 +54,8 @@ from esphome.util import Registry, SimpleRegistry
|
||||
|
||||
AUTO_LOAD = ["binary_sensor"]
|
||||
|
||||
DOMAIN = "remote_base"
|
||||
|
||||
CONF_RECEIVER_ID = "receiver_id"
|
||||
CONF_TRANSMITTER_ID = "transmitter_id"
|
||||
CONF_FIRST = "first"
|
||||
@@ -97,19 +101,54 @@ REMOTE_TRANSMITTABLE_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
# Listener and dumper lists are StaticVectors sized from these counts, so every
|
||||
# registration must go through add_listener / add_dumper
|
||||
_request_listener_slot = cg.slot_counter("REMOTE_BASE_LISTENER_COUNT")
|
||||
_request_dumper_slot = cg.slot_counter("REMOTE_BASE_DUMPER_COUNT")
|
||||
# Listener and dumper lists are StaticVectors sized from these counts, so every registration
|
||||
# must go through add_listener / add_dumper. Every receiver's list gets the same capacity, so
|
||||
# the define is the largest count any one receiver needs, not the sum over all receivers.
|
||||
LISTENER_COUNT_DEFINE = "REMOTE_BASE_LISTENER_COUNT"
|
||||
DUMPER_COUNT_DEFINE = "REMOTE_BASE_DUMPER_COUNT"
|
||||
|
||||
|
||||
@dataclass
|
||||
class _SlotCounts:
|
||||
per_receiver: dict[str, dict[str, int]] = field(default_factory=dict)
|
||||
emitted: bool = False
|
||||
|
||||
|
||||
def _get_slot_counts() -> _SlotCounts:
|
||||
if DOMAIN not in CORE.data:
|
||||
CORE.data[DOMAIN] = _SlotCounts()
|
||||
return CORE.data[DOMAIN]
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.FINAL)
|
||||
async def _emit_slot_counts() -> None:
|
||||
state = _get_slot_counts()
|
||||
state.emitted = True
|
||||
for define, counts in state.per_receiver.items():
|
||||
cg.add_define(define, max(counts.values()))
|
||||
|
||||
|
||||
def _request_slot(define: str, receiver: MockObj) -> None:
|
||||
state = _get_slot_counts()
|
||||
if state.emitted:
|
||||
raise ValueError(
|
||||
f"{define}: slot requested after the count define was emitted; "
|
||||
"request slots from to_code, not from a job running after FINAL"
|
||||
)
|
||||
if not state.per_receiver:
|
||||
CORE.add_job(_emit_slot_counts)
|
||||
counts = state.per_receiver.setdefault(define, {})
|
||||
key = str(receiver)
|
||||
counts[key] = counts.get(key, 0) + 1
|
||||
|
||||
|
||||
def add_listener(receiver: MockObj, listener: MockObj) -> None:
|
||||
_request_listener_slot()
|
||||
_request_slot(LISTENER_COUNT_DEFINE, receiver)
|
||||
cg.add(receiver.register_listener(listener))
|
||||
|
||||
|
||||
def add_dumper(receiver: MockObj, dumper: MockObj) -> None:
|
||||
_request_dumper_slot()
|
||||
_request_slot(DUMPER_COUNT_DEFINE, receiver)
|
||||
cg.add(receiver.register_dumper(dumper))
|
||||
|
||||
|
||||
@@ -118,6 +157,18 @@ async def register_listener(var: MockObj, config: ConfigType) -> None:
|
||||
add_listener(receiver, var)
|
||||
|
||||
|
||||
async def attach_receiver(
|
||||
var: MockObj, config: ConfigType, key: str = CONF_RECEIVER_ID
|
||||
) -> None:
|
||||
"""Link the configured receiver to an entity and register the entity as its listener.
|
||||
|
||||
The C++ set_receiver() no longer registers the listener; the slot for it is counted here.
|
||||
"""
|
||||
receiver = await cg.get_variable(config[key])
|
||||
cg.add(var.set_receiver(receiver))
|
||||
add_listener(receiver, var)
|
||||
|
||||
|
||||
async def register_transmittable(var, config):
|
||||
transmitter_ = await cg.get_variable(config[CONF_TRANSMITTER_ID])
|
||||
cg.add(var.set_transmitter(transmitter_))
|
||||
|
||||
@@ -5,16 +5,18 @@ esp32:
|
||||
board: esp32dev
|
||||
|
||||
remote_receiver:
|
||||
- id: rcvr
|
||||
- id: rcvr_ir
|
||||
pin: GPIO4
|
||||
- id: rcvr_rf
|
||||
pin: GPIO5
|
||||
|
||||
infrared:
|
||||
- platform: ir_rf_proxy
|
||||
name: IR Receiver
|
||||
remote_receiver_id: rcvr
|
||||
remote_receiver_id: rcvr_ir
|
||||
|
||||
radio_frequency:
|
||||
- platform: ir_rf_proxy
|
||||
name: RF Receiver
|
||||
frequency: 433.92MHz
|
||||
remote_receiver_id: rcvr
|
||||
remote_receiver_id: rcvr_rf
|
||||
|
||||
@@ -37,8 +37,9 @@ def test_proxy_receivers_count_as_listeners(
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
generate_main(component_config_path("receiver_with_proxies.yaml"))
|
||||
# infrared and radio_frequency ir_rf_proxy platforms each listen
|
||||
assert get_define_value("REMOTE_BASE_LISTENER_COUNT") == "2"
|
||||
# one proxy entity listens on each of the two receivers; every receiver's list gets the
|
||||
# capacity of the busiest one, so this is the largest per receiver count, not the sum
|
||||
assert get_define_value("REMOTE_BASE_LISTENER_COUNT") == "1"
|
||||
assert get_define_value("REMOTE_BASE_DUMPER_COUNT") is None
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user