From bed58f5895cf687f57727ccdab1273e55deddc07 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 16 Oct 2026 11:07:32 -1000 Subject: [PATCH] [ld6002b] Share one generated function per platform, picking the type by entity pointer --- esphome/components/ld6002b/__init__.py | 49 ++++++++++++++++++- esphome/components/ld6002b/button/__init__.py | 17 +++++-- esphome/components/ld6002b/number/__init__.py | 46 +++++++++-------- esphome/components/ld6002b/select/__init__.py | 24 +++++---- esphome/components/ld6002b/switch/__init__.py | 23 +++++---- 5 files changed, 114 insertions(+), 45 deletions(-) diff --git a/esphome/components/ld6002b/__init__.py b/esphome/components/ld6002b/__init__.py index c7529f93149..4c21d6249c2 100644 --- a/esphome/components/ld6002b/__init__.py +++ b/esphome/components/ld6002b/__init__.py @@ -1,9 +1,12 @@ +from collections.abc import Awaitable, Callable + from esphome import pins import esphome.codegen as cg from esphome.components import uart import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_WAKEUP_PIN -from esphome.types import ConfigType +from esphome.cpp_generator import MockObj +from esphome.types import ConfigType, Expression from .const import CONF_AUTO_WAKE, CONF_WAKEUP_PULSE @@ -73,3 +76,47 @@ async def to_code(config: ConfigType) -> None: cg.add(var.set_wakeup_pulse_ms(config[CONF_WAKEUP_PULSE].total_milliseconds)) cg.add(var.set_auto_wake(config[CONF_AUTO_WAKE])) + + +# (type, entity config, creates the entity from its template arguments, hub setter or None) +DispatchEntity = tuple[ + MockObj, + ConfigType, + Callable[[cg.TemplateArguments], Awaitable[MockObj]], + Callable[[MockObj], Expression] | None, +] + + +async def build_type_dispatch( + hub: MockObj, + params: str, + call: str, + entities: list[DispatchEntity], + publish_before: str | None = None, + publish_after: str | None = None, +) -> None: + """Give all of a platform's entities one generated function that finds the type by pointer. + + One class per domain instead of one per entity; ``call`` names the type as ``type``. + """ + if not entities: + return + fn = f"esphome__{entities[0][1][CONF_ID].id}__fn" + # Declared first: the entities' types name it and its body names the entities. + cg.add_global(cg.RawStatement(f"static void {fn}({params});")) + template = cg.TemplateArguments(cg.RawExpression(fn)) + created: list[tuple[MockObj, MockObj]] = [] + for type_, _, create, setter in entities: + var = await create(template) + if setter is not None: + cg.add(setter(var)) + created.append((type_, var)) + *head, (last_type, _) = created + chain = "".join(f"entity == ::{var} ? {type_} : " for type_, var in head) + body = [f"const auto type = {chain}{last_type};", f"::{hub}->{call};"] + if publish_before: + body.insert(0, publish_before) + if publish_after: + body.append(publish_after) + lines = "\n ".join(body) + cg.add_global(cg.RawStatement(f"static void {fn}({params}) {{\n {lines}\n}}")) diff --git a/esphome/components/ld6002b/button/__init__.py b/esphome/components/ld6002b/button/__init__.py index 294e08d972b..bbbee0c8472 100644 --- a/esphome/components/ld6002b/button/__init__.py +++ b/esphome/components/ld6002b/button/__init__.py @@ -1,3 +1,6 @@ +from functools import partial + +import esphome.codegen as cg from esphome.components import button from esphome.components.select import DOMAIN as SELECT_DOMAIN import esphome.config_validation as cv @@ -10,7 +13,7 @@ from esphome.const import ( import esphome.final_validate as fv from esphome.types import ConfigType -from .. import LD6002BComponent, ld6002b_ns +from .. import LD6002BComponent, build_type_dispatch, ld6002b_ns from ..const import ( CONF_APPLY_AREA, CONF_AUTO_INTERFERENCE, @@ -128,6 +131,12 @@ BUTTON_MAP = { async def to_code(config: ConfigType) -> None: - buttons = button.sub_buttons(config, parent=config[CONF_LD6002B_ID]) - for key, button_type in BUTTON_MAP.items(): - await buttons(key, None, apply=f"press_button({button_type})") + hub = await cg.get_variable(config[CONF_LD6002B_ID]) + entities = [ + (button_type, conf, partial(button.new_button, conf), None) + for key, button_type in BUTTON_MAP.items() + if (conf := config.get(key)) is not None + ] + await build_type_dispatch( + hub, "button::Button *entity", "press_button(type)", entities + ) diff --git a/esphome/components/ld6002b/number/__init__.py b/esphome/components/ld6002b/number/__init__.py index 041cb0cb3b3..f335c708189 100644 --- a/esphome/components/ld6002b/number/__init__.py +++ b/esphome/components/ld6002b/number/__init__.py @@ -1,3 +1,5 @@ +from functools import partial + import esphome.codegen as cg from esphome.components import number from esphome.components.button import DOMAIN as BUTTON_DOMAIN @@ -15,7 +17,7 @@ from esphome.const import ( import esphome.final_validate as fv from esphome.types import ConfigType -from .. import LD6002BComponent, ld6002b_ns +from .. import DispatchEntity, LD6002BComponent, build_type_dispatch, ld6002b_ns from ..const import ( CONF_APPLY_AREA, CONF_AREA_CONFIG, @@ -138,7 +140,7 @@ FINAL_VALIDATE_SCHEMA = final_validate async def to_code(config: ConfigType) -> None: hub = await cg.get_variable(config[CONF_LD6002B_ID]) - numbers = number.sub_numbers(config, parent=hub) + entities: list[DispatchEntity] = [] for key, number_type, setter, min_value, max_value, step in ( (CONF_HOLD_DELAY, NumberType.HOLD_DELAY, "set_hold_delay_number", 0, 65535, 1), @@ -156,18 +158,17 @@ async def to_code(config: ConfigType) -> None: 100, ), ): - await numbers( - key, - getattr(hub, setter), - min_value=min_value, - max_value=max_value, - step=step, - apply=f"set_number_value({number_type}, value)", - publish="before", - ) + if (conf := config.get(key)) is not None: + create = partial( + number.new_number, + conf, + min_value=min_value, + max_value=max_value, + step=step, + ) + entities.append((number_type, conf, create, getattr(hub, setter))) if area_config := config.get(CONF_AREA_CONFIG): - area_numbers = number.sub_numbers(area_config, parent=hub) for key, number_type, setter in ( (KEY_X_MIN, NumberType.AREA_X_MIN, "set_area_x_min_number"), (KEY_X_MAX, NumberType.AREA_X_MAX, "set_area_x_max_number"), @@ -176,12 +177,15 @@ async def to_code(config: ConfigType) -> None: (CONF_Z_MIN, NumberType.AREA_Z_MIN, "set_area_z_min_number"), (CONF_Z_MAX, NumberType.AREA_Z_MAX, "set_area_z_max_number"), ): - await area_numbers( - key, - getattr(hub, setter), - min_value=-10, - max_value=10, - step=0.1, - apply=f"set_number_value({number_type}, value)", - publish="before", - ) + if (conf := area_config.get(key)) is not None: + create = partial( + number.new_number, conf, min_value=-10, max_value=10, step=0.1 + ) + entities.append((number_type, conf, create, getattr(hub, setter))) + await build_type_dispatch( + hub, + "number::Number *entity, float value", + "set_number_value(type, value)", + entities, + publish_before="entity->publish_state(value);", + ) diff --git a/esphome/components/ld6002b/select/__init__.py b/esphome/components/ld6002b/select/__init__.py index 60a030e2649..281b7579fc2 100644 --- a/esphome/components/ld6002b/select/__init__.py +++ b/esphome/components/ld6002b/select/__init__.py @@ -1,10 +1,12 @@ +from functools import partial + import esphome.codegen as cg from esphome.components import select import esphome.config_validation as cv from esphome.const import CONF_AREA_ID, CONF_SENSITIVITY, ENTITY_CATEGORY_CONFIG from esphome.types import ConfigType -from .. import LD6002BComponent, ld6002b_ns +from .. import DispatchEntity, LD6002BComponent, build_type_dispatch, ld6002b_ns from ..const import CONF_INSTALLATION_MODE, CONF_LD6002B_ID, CONF_TRIGGER_SPEED DEPENDENCIES = ["ld6002b"] @@ -66,13 +68,15 @@ SELECT_MAP = ( async def to_code(config: ConfigType) -> None: hub = await cg.get_variable(config[CONF_LD6002B_ID]) - selects = select.sub_selects(config, parent=hub) - + entities: list[DispatchEntity] = [] for key, select_type, setter, options in SELECT_MAP: - await selects( - key, - getattr(hub, setter), - options=options, - apply=f"set_select_value({select_type}, index)", - publish="before", - ) + if (conf := config.get(key)) is not None: + create = partial(select.new_select, conf, options=options) + entities.append((select_type, conf, create, getattr(hub, setter))) + await build_type_dispatch( + hub, + "select::Select *entity, size_t index", + "set_select_value(type, index)", + entities, + publish_before="entity->publish_state(index);", + ) diff --git a/esphome/components/ld6002b/switch/__init__.py b/esphome/components/ld6002b/switch/__init__.py index b6c89d3cfa4..7ee4612c832 100644 --- a/esphome/components/ld6002b/switch/__init__.py +++ b/esphome/components/ld6002b/switch/__init__.py @@ -1,10 +1,12 @@ +from functools import partial + import esphome.codegen as cg from esphome.components import switch import esphome.config_validation as cv from esphome.const import DEVICE_CLASS_SWITCH, ENTITY_CATEGORY_CONFIG from esphome.types import ConfigType -from .. import LD6002BComponent, ld6002b_ns +from .. import DispatchEntity, LD6002BComponent, build_type_dispatch, ld6002b_ns from ..const import ( CONF_LD6002B_ID, CONF_LOW_POWER, @@ -48,16 +50,19 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config: ConfigType) -> None: hub = await cg.get_variable(config[CONF_LD6002B_ID]) - switches = switch.sub_switches(config, parent=hub) - + entities: list[DispatchEntity] = [] for key, switch_type, setter in ( (CONF_LOW_POWER, SwitchType.LOW_POWER, "set_low_power_switch"), (CONF_POINT_CLOUD, SwitchType.POINT_CLOUD, "set_point_cloud_switch"), (CONF_TARGET_DISPLAY, SwitchType.TARGET_DISPLAY, "set_target_display_switch"), ): - await switches( - key, - getattr(hub, setter), - apply=f"set_switch_state({switch_type}, state)", - publish="after", - ) + if (conf := config.get(key)) is not None: + create = partial(switch.new_switch, conf) + entities.append((switch_type, conf, create, getattr(hub, setter))) + await build_type_dispatch( + hub, + "switch_::Switch *entity, bool state", + "set_switch_state(type, state)", + entities, + publish_after="entity->publish_state(state);", + )