mirror of
https://github.com/esphome/esphome.git
synced 2026-10-01 17:00:21 +00:00
[bluetooth_proxy] Migrate esp32 onto the neutral GATT backend (#18198)
This commit is contained in:
@@ -15,6 +15,13 @@ import voluptuous as vol
|
||||
from esphome import config_validation as cv
|
||||
from esphome.components.bluetooth_proxy import CONFIG_SCHEMA, _esp32_config_schema
|
||||
|
||||
|
||||
def _esp32_schema_keys() -> dict[str, object]:
|
||||
# The builder names its platform explicitly, so no CORE state is needed
|
||||
# (this also mirrors how the language-schema dumper calls it).
|
||||
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),
|
||||
# generated IDs (not user-walkable options), and connections (must validate
|
||||
@@ -38,7 +45,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 +58,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()
|
||||
|
||||
@@ -9,11 +9,13 @@ import pytest
|
||||
|
||||
from esphome import config_validation as cv
|
||||
from esphome.components import ble_device_base, bluetooth_connection, bluetooth_proxy
|
||||
from esphome.config_helpers import frameworks_for_platforms
|
||||
from esphome.const import (
|
||||
CONF_ACTIVE,
|
||||
KEY_CORE,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_LN882X,
|
||||
PLATFORM_RP2,
|
||||
PlatformFramework,
|
||||
@@ -177,28 +179,49 @@ def test_rp2_rejects_esp32_only_keys_by_name(
|
||||
bluetooth_proxy.CONFIG_SCHEMA({"connections": [{}]})
|
||||
|
||||
|
||||
def test_hub_source_filter_covers_every_hub_platform() -> None:
|
||||
# bluetooth_connection cannot import this module to derive the hub.cpp
|
||||
# framework set, so pin it here: a platform admitted to the proxy but
|
||||
# missing from the filter would validate, then fail at link.
|
||||
expected = frameworks_for_platforms(
|
||||
[*bluetooth_proxy._HUB_PLATFORMS, PLATFORM_ESP32]
|
||||
)
|
||||
hub_frameworks = bluetooth_connection.SOURCE_FILE_FRAMEWORKS[
|
||||
"bluetooth_connection_hub.cpp"
|
||||
]
|
||||
assert expected == hub_frameworks
|
||||
|
||||
|
||||
def test_bluetooth_connection_auto_load_covers_its_includes() -> None:
|
||||
# The esp32 connection header includes esp32_ble_client; the auto load
|
||||
# must satisfy that closure itself (regression: it once relied on the
|
||||
# consumer's auto loads).
|
||||
# The backend registers with its platform BLE stack (and the Bluedroid
|
||||
# header includes the tracker's), so that closure lives here and
|
||||
# consumers stay platform-blind; the platform-less arm is the union for
|
||||
# manifest-resolving tooling.
|
||||
_set_platform("esp32")
|
||||
assert "esp32_ble_client" in bluetooth_connection.AUTO_LOAD()
|
||||
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"]
|
||||
# No target platform (tooling resolving the manifest): the union, so
|
||||
# dependency closures stay complete for build_codeowners and friends.
|
||||
_set_platform(None)
|
||||
assert bluetooth_connection.AUTO_LOAD() == ["ble_device_base", "esp32_ble_client"]
|
||||
assert bluetooth_connection.AUTO_LOAD() == [
|
||||
"ble_device_base",
|
||||
"esp32_ble_tracker",
|
||||
"rp2040_ble",
|
||||
]
|
||||
|
||||
|
||||
def test_every_registered_hub_platform_has_a_schema_arm() -> None:
|
||||
# A platform added to HUB_MAX_CONNECTIONS without a schema builder,
|
||||
# codegen arm, or _HUB_PLATFORMS entry would only fail when a config for
|
||||
# it is validated (or not even then); pin all three couplings here.
|
||||
# A platform added to HUB_MAX_CONNECTIONS without a schema builder or
|
||||
# _HUB_PLATFORMS entry would only fail when a config for it is validated
|
||||
# (or not even then); pin both couplings here. Connection codegen is
|
||||
# shared (bluetooth_connection.new_gatt_backend), so it needs no arm.
|
||||
registered = set(bluetooth_connection.HUB_MAX_CONNECTIONS)
|
||||
assert registered <= set(bluetooth_proxy._GATT_HUB_SCHEMAS)
|
||||
assert registered <= set(bluetooth_proxy._GATT_HUB_TO_CODE)
|
||||
assert registered <= set(bluetooth_proxy._HUB_PLATFORMS)
|
||||
# Hub platforms must also be in the backend registry the shared codegen
|
||||
# helpers dispatch on.
|
||||
assert registered <= set(bluetooth_connection._PLATFORM_BACKENDS)
|
||||
# The outer walkable schema's bound must stay the loosest platform cap.
|
||||
assert (
|
||||
max(bluetooth_connection.HUB_MAX_CONNECTIONS.values())
|
||||
@@ -220,9 +243,14 @@ def test_defines_h_mirrors_the_rp2_slot_cap() -> None:
|
||||
assert int(match.group(1)) == cap, (
|
||||
f"defines.h rp2 arm carries {match.group(1)}, expected {cap}"
|
||||
)
|
||||
# The static-analysis client count scales with the same cap.
|
||||
match = re.search(r"#define ESPHOME_BLE_GATT_CLIENT_COUNT (\d+)", defines)
|
||||
assert match is not None, "ESPHOME_BLE_GATT_CLIENT_COUNT missing from defines.h"
|
||||
# The static-analysis client count scales with the same cap. Scoped to
|
||||
# the USE_RP2 block: the esp32 arm carries its own count.
|
||||
rp2_block = re.search(r"#ifdef USE_RP2\n((?:#define [^\n]*\n)+)", defines)
|
||||
assert rp2_block is not None, "no USE_RP2 platform block in defines.h"
|
||||
match = re.search(
|
||||
r"#define ESPHOME_BLE_GATT_CLIENT_COUNT (\d+)", rp2_block.group(1)
|
||||
)
|
||||
assert match is not None, "ESPHOME_BLE_GATT_CLIENT_COUNT missing from rp2 block"
|
||||
assert int(match.group(1)) == cap, (
|
||||
f"ESPHOME_BLE_GATT_CLIENT_COUNT is {match.group(1)}, expected {cap}"
|
||||
f"rp2 ESPHOME_BLE_GATT_CLIENT_COUNT is {match.group(1)}, expected {cap}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user