From 4ca50d86bc47309c0a39fbf9dba14137437b1bae Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Sep 2026 04:38:10 -0500 Subject: [PATCH] [remote_base] Reject unknown protocol names and log a dropped secondary dumper --- esphome/components/remote_base/__init__.py | 14 +++++++++----- esphome/components/remote_base/remote_base.cpp | 15 ++++++++------- .../remote_receiver/test_slot_counts.py | 6 ++++++ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index fba9e834ca..7edb8b042f 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -136,15 +136,19 @@ def protocol_define(name: str) -> str: return f"USE_REMOTE_PROTOCOL_{_protocol_stem(name).upper()}" -def request_protocol(name: str) -> None: - """Keep a protocol's source file in the build; components using it from C++ must call this.""" - cg.add_define(protocol_define(name)) - - _PROTOCOL_STEMS = sorted( path.name.removesuffix("_protocol.cpp") for path in Path(__file__).parent.glob("*_protocol.cpp") ) + + +def request_protocol(name: str) -> None: + """Keep a protocol's source file in the build; components using it from C++ must call this.""" + if _protocol_stem(name) not in _PROTOCOL_STEMS: + raise ValueError(f"Unknown remote protocol {name!r}") + cg.add_define(protocol_define(name)) + + # Only the protocol sources a configuration uses are compiled FILTER_SOURCE_FILES = filter_source_files_from_defines( {f"{stem}_protocol.cpp": protocol_define(stem) for stem in _PROTOCOL_STEMS} diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index 2e3f292940..ea6a8fe9c0 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -114,15 +114,16 @@ void RemoteReceiverBase::register_listener(RemoteReceiverListener *listener) { #ifdef REMOTE_BASE_DUMPER_COUNT void RemoteReceiverBase::register_dumper(RemoteReceiverDumperBase *dumper) { if (dumper->is_secondary()) { - this->secondary_dumper_ = dumper; + if (this->secondary_dumper_ == nullptr) { + this->secondary_dumper_ = dumper; + return; + } + } else if (this->dumpers_.size() < REMOTE_BASE_DUMPER_COUNT) { + this->dumpers_.push_back(dumper); return; } - if (this->dumpers_.size() == REMOTE_BASE_DUMPER_COUNT) { - ESP_LOGE(TAG, "No %s slot: register it from to_code() with remote_base.add_%s", LOG_STR_LITERAL("dumper"), - LOG_STR_LITERAL("dumper")); - return; - } - this->dumpers_.push_back(dumper); + ESP_LOGE(TAG, "No %s slot: register it from to_code() with remote_base.add_%s", LOG_STR_LITERAL("dumper"), + LOG_STR_LITERAL("dumper")); } #endif diff --git a/tests/component_tests/remote_receiver/test_slot_counts.py b/tests/component_tests/remote_receiver/test_slot_counts.py index 1815185784..9c80c3674f 100644 --- a/tests/component_tests/remote_receiver/test_slot_counts.py +++ b/tests/component_tests/remote_receiver/test_slot_counts.py @@ -71,6 +71,12 @@ def test_every_registry_name_maps_to_a_protocol_source() -> None: assert f"{stem}_protocol.cpp" in sources, name +def test_request_protocol_rejects_unknown_names() -> None: + """A misspelled protocol would otherwise surface only as a link error.""" + with pytest.raises(ValueError, match="toshiba"): + remote_base.request_protocol("toshiba") + + def test_dump_list_is_deduplicated_across_forms() -> None: dumpers = remote_base.validate_dumpers(["raw", {"raw": None}, "nec", "nec"]) assert [name for name, _ in dumpers] == ["raw", "nec"]