Compare commits

...
Author SHA1 Message Date
J. Nick Koston bc451c933b Merge remote-tracking branch 'origin/dev' into remote-base-flash-dumpers-v2
# Conflicts:
#	esphome/components/remote_base/__init__.py
#	tests/component_tests/remote_receiver/test_slot_counts.py
2026-10-24 15:01:53 -10:00
J. Nick Koston c74e02eba8 Merge remote-tracking branch 'origin/remote-base-dumper-table' into remote-base-flash-dumpers-v2
# Conflicts:
#	esphome/components/remote_base/__init__.py
#	tests/component_tests/remote_receiver/test_slot_counts.py
2026-10-24 14:51:49 -10:00
J. Nick Koston ea4ee0c4e0 [remote_base] Let the slot counter guard late dumpers and drop the duplicate flag 2026-10-24 14:48:38 -10:00
J. Nick Koston 1edb99424b [remote_base] Keep the dumper tables under CORE.data[DOMAIN] 2026-10-24 14:48:38 -10:00
J. Nick Koston 146bed3a98 [remote_base] Fix pylint shadowing and brace the warning log 2026-10-24 14:48:38 -10:00
J. Nick Koston f6fb0171c5 [remote_base] Report extra secondary dumpers from dump_config 2026-10-24 14:48:38 -10:00
J. Nick Koston 274abdac3c [remote_base] Warn when a receiver has more than one secondary dumper 2026-10-24 14:48:38 -10:00
J. Nick Koston 8cfafc3fe8 [remote_base] Keep the YAML dumpers ahead of dumpers registered from another to_code 2026-10-24 14:48:38 -10:00
J. Nick Koston 533b181d4d [remote_base] Give each receiver an exact dumper table instead of a shared-size StaticVector 2026-10-24 14:48:38 -10:00
J. Nick Koston 481648a512 Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2 2026-10-23 22:11:35 -10:00
J. Nick Koston 3932ecc9b3 Merge branch 'const-vector-append-copy' into remote-base-dumper-table-v2 2026-10-23 22:11:34 -10:00
J. Nick Koston af7c68b35d Merge commit '5e30279ab64986656a5f869d1173434c951eac33' into const-vector-append-copy 2026-10-23 22:11:34 -10:00
J. Nick Koston 5e30279ab6 Merge remote-tracking branch 'origin/dev' into HEAD 2026-10-23 22:11:34 -10:00
J. Nick Koston 760ed98f42 Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2 2026-10-23 21:58:04 -10:00
J. Nick Koston 9bc5efd7fc Merge branch 'const-vector-append-copy' into remote-base-dumper-table-v2 2026-10-23 21:58:04 -10:00
J. Nick Koston 160481acf7 [core] Keep the static copy helper without a trailing underscore for clang-tidy 2026-10-23 21:58:04 -10:00
J. Nick Koston 8a17d3c14a Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2 2026-10-23 21:54:27 -10:00
J. Nick Koston cb5140cd57 Merge branch 'const-vector-append-copy' into remote-base-dumper-table-v2 2026-10-23 21:54:27 -10:00
J. Nick Koston 25c6bf837b [core] Say why ConstVector elements must be whole words 2026-10-23 21:54:27 -10:00
J. Nick Koston 2c392c7e8b [remote_base] Share one flash object and one size check per dumper type 2026-10-23 21:53:09 -10:00
J. Nick Koston 651c3621ea Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2
# Conflicts:
#	tests/component_tests/remote_receiver/test_slot_counts.py
2026-10-23 21:53:08 -10:00
J. Nick Koston 67f31ccf85 [remote_base] Let the slot counter guard late dumpers and drop the duplicate flag 2026-10-23 21:52:00 -10:00
J. Nick Koston 6c51825569 Merge branch 'const-vector-append-copy' into remote-base-dumper-table-v2 2026-10-23 21:51:09 -10:00
J. Nick Koston c4224dcbe0 [core] Name the protected copy helper with a trailing underscore 2026-10-23 21:51:04 -10:00
J. Nick Koston 959ac66b26 Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2 2026-10-23 21:46:48 -10:00
J. Nick Koston 2cdc85b20a Merge branch 'const-vector-append-copy' into remote-base-dumper-table-v2 2026-10-23 21:46:47 -10:00
J. Nick Koston 9bebff7228 [core] Copy with std::copy off ESP8266, which clang-tidy accepts for pointer tables 2026-10-23 21:46:47 -10:00
J. Nick Koston 9fbe04746c [remote_base] Give flash dumpers a unique storage name and keep their ids usable in lambdas 2026-10-23 21:40:37 -10:00
J. Nick Koston ceb65f61c1 Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2
# Conflicts:
#	tests/component_tests/remote_receiver/test_slot_counts.py
2026-10-23 21:38:56 -10:00
J. Nick Koston 05cddd8157 [remote_base] Keep the dumper tables under CORE.data[DOMAIN] 2026-10-23 21:38:47 -10:00
J. Nick Koston cb6be0d6ca Merge branch 'const-vector-append-copy' into remote-base-dumper-table-v2 2026-10-23 21:38:32 -10:00
J. Nick Koston 5295a51d2f [core] Copy ConstVector tables with memcpy_P or memcpy instead of word loads 2026-10-23 21:38:25 -10:00
J. Nick Koston 097b50256c Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2 2026-10-23 20:55:53 -10:00
J. Nick Koston 7837d212c4 [remote_base] Fix pylint shadowing and brace the warning log 2026-10-23 20:55:53 -10:00
J. Nick Koston c1498a280d [core] Name the static copy helper without the trailing underscore 2026-10-23 20:55:09 -10:00
J. Nick Koston 0334236c73 Merge branch 'const-vector-append-copy' into remote-base-dumper-table-v2 2026-10-23 20:55:09 -10:00
J. Nick Koston 9b7f9057e7 [remote_base] Also require a trivial destructor for a flash dumper 2026-10-23 20:53:39 -10:00
J. Nick Koston 3220915d82 [remote_base] Report extra secondary dumpers from dump_config 2026-10-23 20:52:27 -10:00
J. Nick Koston fb37dc8b75 Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2 2026-10-23 20:52:27 -10:00
J. Nick Koston 0ac066405f [remote_base] Keep every dumper in flash; external protocol dumpers are stateless too 2026-10-23 20:49:07 -10:00
J. Nick Koston c2b23311f2 [remote_base] Warn when a receiver has more than one secondary dumper 2026-10-23 20:43:14 -10:00
J. Nick Koston 6d6f7bd2f8 Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2 2026-10-23 20:43:14 -10:00
J. Nick Koston af788e2c37 [remote_base] Fail the build when a flash dumper gains a data member 2026-10-23 20:38:31 -10:00
J. Nick Koston 7e40bcd334 [remote_base] Keep the YAML dumpers ahead of dumpers registered from another to_code 2026-10-23 20:34:34 -10:00
J. Nick Koston 6069b820fa Merge branch 'remote-base-dumper-table-v2' into remote-base-flash-dumpers-v2 2026-10-23 20:34:34 -10:00
J. Nick Koston e263d52405 [core] Copy without uint32_t aliasing, share the copy with assign_copy, and add prepend_copy 2026-10-23 20:34:07 -10:00
J. Nick Koston 2bc39e50eb Merge branch 'const-vector-append-copy' into remote-base-dumper-table-v2 2026-10-23 20:34:07 -10:00
J. Nick Koston 448790f483 [remote_base] Keep the stateless in-tree dumpers in flash 2026-10-23 20:19:30 -10:00
J. Nick Koston a9c8d052df [remote_base] Give each receiver an exact dumper table instead of a shared-size StaticVector 2026-10-23 20:19:30 -10:00
J. Nick Koston f77796877d [core] Add ConstVector::append_copy that reads flash tables a word at a time 2026-10-23 20:18:52 -10:00
J. Nick Koston 2931a58a9b [remote_base] Test dumper dispatch and registration 2026-10-23 20:04:46 -10:00
3 changed files with 104 additions and 6 deletions

No files matched your search

+43 -3
View File
@@ -1,4 +1,6 @@
from collections.abc import Callable
import dataclasses
from dataclasses import dataclass
from pathlib import Path
from typing import Any
@@ -132,9 +134,22 @@ def add_listener(receiver: MockObj, listener: MockObj) -> None:
cg.add(receiver.register_listener(listener))
@dataclass
class _RemoteBaseData:
# receiver id -> (receiver, its dumpers in registration order)
tables: dict[str, tuple[MockObj, list[MockObj]]] = dataclasses.field(
default_factory=dict
)
# dumper type -> its one flash object, shared by every receiver that lists it
flash_dumpers: dict[str, str] = dataclasses.field(default_factory=dict)
def _get_data() -> _RemoteBaseData:
return CORE.data.setdefault(DOMAIN, _RemoteBaseData())
def _get_dumper_tables() -> dict[str, tuple[MockObj, list[MockObj]]]:
"""Receiver id -> (receiver, its dumpers in registration order)."""
return CORE.data.setdefault(DOMAIN, {})
return _get_data().tables
@coroutine_with_priority(CoroPriority.FINAL)
@@ -255,7 +270,32 @@ def register_trigger(name, type, data_type):
return decorator
def _new_flash_dumper(dumper_id: ID) -> cg.RawExpression:
"""Place a stateless dumper in flash; the receiver only ever calls it."""
flash_dumpers = _get_data().flash_dumpers
if (storage := flash_dumpers.get(str(dumper_id.type))) is None:
# A generated name a config id cannot take; registered so later globals avoid it too
storage_id = cg.unique_global_id(f"{dumper_id.id}__flash", dumper_id.type)
storage = flash_dumpers[str(dumper_id.type)] = storage_id.id
CORE.register_variable(storage_id, cg.MockObj(storage))
CORE.add_global_after_includes(
cg.RawStatement(
f"static_assert(sizeof({dumper_id.type}) == sizeof(void *) && "
f"std::is_trivially_destructible_v<{dumper_id.type}>, "
'"a dumper kept in flash must have no data members or destructor");'
)
)
CORE.add_global_after_includes(
cg.RawStatement(f"static constexpr {dumper_id.type} {storage} PROGMEM{{}};")
)
# A constant address for the receiver's flash table; lambdas get a pointer-like object
address = f"const_cast<{dumper_id.type} *>(&{storage})"
CORE.register_variable(dumper_id, cg.MockObj(address, "->"))
return cg.RawExpression(address)
def register_dumper(name, type, schema=None):
"""Dumpers are stateless, so each is a flash object; one with data members fails the build."""
if schema is None:
schema = {}
registerer = DUMPER_REGISTRY.register(name, type, schema)
@@ -263,7 +303,7 @@ def register_dumper(name, type, schema=None):
def decorator(func):
async def new_func(config, dumper_id):
_request_protocol_if_in_tree(name)
var = cg.new_Pvariable(dumper_id)
var = _new_flash_dumper(dumper_id)
await coroutine(func)(var, config)
return var
@@ -0,0 +1,21 @@
esphome:
name: test
on_boot:
- lambda: |-
ESP_LOGD("test", "secondary %d", id(my_nec).is_secondary());
esp32:
board: esp32dev
logger:
remote_receiver:
- id: ir_rcvr
pin: GPIO4
dump:
- nec:
type_id: my_nec
- id: my_nec__flash
pin: GPIO5
dump:
- rc_switch
@@ -149,15 +149,20 @@ def test_dumper_count_is_the_busiest_receivers(
@pytest.mark.usefixtures("restore_protocol_registries")
def test_external_dumpers_are_constructed_in_ram(
def test_external_dumpers_live_in_flash(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""A dumper registered outside remote_base may hold state."""
"""External protocol dumpers are stateless too; the size check guards one that is not."""
main_cpp = generate_main(
component_config_path("receiver_with_external_protocol.yaml")
)
assert re.search(r"new\(\w+\) fake_protocol::FakeDumper\(\);", main_cpp)
assert re.search(
r"static constexpr fake_protocol::FakeDumper \w+ PROGMEM\{\};", main_cpp
)
assert (
"static_assert(sizeof(fake_protocol::FakeDumper) == sizeof(void *)" in main_cpp
)
def test_each_receiver_gets_its_own_dumper_table(
@@ -178,3 +183,35 @@ def test_add_dumper_after_the_tables_were_emitted_raises(
generate_main(component_config_path("receiver_two_dumper_lists.yaml"))
with pytest.raises(ValueError, match="after the count define was emitted"):
remote_base.add_dumper(MockObj("rcvr", "->"), MockObj("dumper", "->"))
def test_in_tree_dumpers_live_in_flash(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
main_cpp = generate_main(component_config_path("receiver_two_dumper_lists.yaml"))
assert re.search(
r"static constexpr remote_base::NECDumper \w+ PROGMEM\{\};", main_cpp
)
assert not re.search(r"new\(\w+\) remote_base::\w+Dumper\(\);", main_cpp)
# A dumper that gains a member must fail the build, not fault on the device
assert "static_assert(sizeof(remote_base::NECDumper) == sizeof(void *)" in main_cpp
# rc_switch is on both receivers but has one flash object and one size check
assert (
len(re.findall(r"static constexpr remote_base::RCSwitchDumper ", main_cpp)) == 1
)
assert main_cpp.count("static_assert(sizeof(remote_base::RCSwitchDumper)") == 1
def test_a_dumper_id_works_in_lambdas_and_cannot_collide(
generate_main: Callable[[str | Path], str],
component_config_path: Callable[[str], Path],
) -> None:
"""A user type_id is usable from a lambda, and a config id cannot take the flash storage name."""
main_cpp = generate_main(component_config_path("receiver_dumper_type_id.yaml"))
assert "const_cast<remote_base::NECDumper *>(&my_nec__flash_" in main_cpp
assert "->is_secondary()" in main_cpp
assert re.search(
r"static constexpr remote_base::NECDumper my_nec__flash_\w+ PROGMEM\{\};",
main_cpp,
)