Own the platform dispatch in bluetooth_connection and gate the proxy surface

This commit is contained in:
J. Nick Koston
2026-08-08 23:16:58 -05:00
parent 5d42a69d7a
commit 2e8700f090
10 changed files with 132 additions and 95 deletions
@@ -14,6 +14,17 @@ import voluptuous as vol
from esphome import config_validation as cv
from esphome.components.bluetooth_proxy import CONFIG_SCHEMA, _esp32_config_schema
from esphome.const import KEY_CORE, KEY_TARGET_PLATFORM, PLATFORM_ESP32
from esphome.core import CORE
def _esp32_schema_keys() -> dict[str, object]:
# The builder resolves the backend schema through the platform-dispatched
# bluetooth_connection.gatt_client_schema(), so the platform must be set
# (conftest's autouse reset restores CORE after each test).
CORE.data.setdefault(KEY_CORE, {})[KEY_TARGET_PLATFORM] = PLATFORM_ESP32
return _keys(_schema_of(_esp32_config_schema()))
# esp32-schema keys with no place in the outer schema: COMPONENT_SCHEMA
# plumbing (derived, so a future core key does not fail this component's test),
@@ -38,7 +49,7 @@ def _keys(schema: vol.Schema) -> dict[str, object]:
def test_outer_scalar_keys_exist_in_esp32_schema() -> None:
outer = _keys(_schema_of(CONFIG_SCHEMA))
esp32 = _keys(_schema_of(_esp32_config_schema()))
esp32 = _esp32_schema_keys()
missing = set(outer) - set(esp32)
assert not missing, (
f"outer CONFIG_SCHEMA declares {sorted(missing)} which the esp32 schema "
@@ -51,7 +62,7 @@ def test_esp32_scalars_all_walkable() -> None:
"""Every non-generated esp32 scalar option must appear in the outer schema
(connections is deliberately excluded — it must validate exactly once)."""
outer = _keys(_schema_of(CONFIG_SCHEMA))
esp32 = _keys(_schema_of(_esp32_config_schema()))
esp32 = _esp32_schema_keys()
scalar = {
name
for name, key in esp32.items()
@@ -178,11 +178,21 @@ def test_rp2_rejects_esp32_only_keys_by_name(
def test_bluetooth_connection_auto_load_covers_its_includes() -> None:
# Every backend builds on ble_device_base alone; the Bluedroid backend
# talks to IDF directly, so esp32_ble_client is no longer in the closure.
for platform in ("esp32", "rp2", None):
_set_platform(platform)
assert bluetooth_connection.AUTO_LOAD() == ["ble_device_base"]
# The backend registers with its platform BLE stack, so that dependency
# lives here and consumers (proxy, radon_eye_rd200) stay platform-blind;
# the platform-less arm is the union for manifest-resolving tooling.
_set_platform("esp32")
assert bluetooth_connection.AUTO_LOAD() == ["ble_device_base", "esp32_ble_tracker"]
_set_platform("rp2")
assert bluetooth_connection.AUTO_LOAD() == ["ble_device_base", "rp2040_ble"]
_set_platform("ln882x")
assert bluetooth_connection.AUTO_LOAD() == ["ble_device_base"]
_set_platform(None)
assert bluetooth_connection.AUTO_LOAD() == [
"ble_device_base",
"esp32_ble_tracker",
"rp2040_ble",
]
def test_every_registered_hub_platform_has_a_schema_arm() -> None: