mirror of
https://github.com/esphome/esphome.git
synced 2026-10-01 08:50:22 +00:00
Merge branch 'dev' into rp2-3-connection-slots
This commit is contained in:
@@ -15,6 +15,7 @@ bk72xx:
|
||||
bk72xx_ble_tracker:
|
||||
scan_parameters:
|
||||
continuous: false
|
||||
active: false
|
||||
on_ble_advertise:
|
||||
- mac_address:
|
||||
- AC:37:43:77:5F:4C
|
||||
|
||||
@@ -48,6 +48,8 @@ def test_trigger_codegen(
|
||||
# scan_parameters continuous: false reaches the YAML-mode setter, not the
|
||||
# runtime override.
|
||||
assert "->set_configured_continuous(false)" in main_cpp
|
||||
# active: false (non-default) flows through to the setter.
|
||||
assert "->set_scan_active(false)" in main_cpp
|
||||
# Constructor call, not just the declaration: the parent argument is what
|
||||
# registers the trigger as a listener.
|
||||
assert re.search(
|
||||
|
||||
@@ -16,8 +16,8 @@ from esphome.components.ln882h_ble_tracker import (
|
||||
from esphome.components.rp2_ble_tracker import SCAN_PARAMETERS_SCHEMA as RP2_SCHEMA
|
||||
|
||||
|
||||
def _validate(**kwargs: str) -> dict:
|
||||
"""Run a scan_parameters config through a passive tracker's real schema."""
|
||||
def _validate(**kwargs: str | bool) -> dict:
|
||||
"""Run a scan_parameters config through the bk72xx tracker's real schema."""
|
||||
return BK72XX_SCHEMA(kwargs)
|
||||
|
||||
|
||||
@@ -48,11 +48,12 @@ def test_to_ble_units_truncates() -> None:
|
||||
|
||||
|
||||
def test_bk72xx_defaults_are_valid() -> None:
|
||||
"""bk72xx pins the BK reference rate: 100 ms interval, shared 30 ms window."""
|
||||
"""bk72xx pins the BK reference rate — 100 ms interval, shared 30 ms window —
|
||||
and exposes active (default on, like every active-capable tracker)."""
|
||||
config = _validate()
|
||||
assert to_ble_units(config["interval"]) == 160
|
||||
assert to_ble_units(config["window"]) == 48
|
||||
assert "active" not in config
|
||||
assert config["active"] is True
|
||||
|
||||
|
||||
def test_esp32_defaults_are_valid() -> None:
|
||||
@@ -86,10 +87,9 @@ def test_esp32_active_can_disable() -> None:
|
||||
assert config["active"] is False
|
||||
|
||||
|
||||
def test_passive_schema_rejects_active_key() -> None:
|
||||
"""Trackers without active scan support must not silently accept the option."""
|
||||
with pytest.raises(cv.Invalid):
|
||||
_validate(active="true")
|
||||
def test_bk72xx_active_can_disable() -> None:
|
||||
config = _validate(active=False)
|
||||
assert config["active"] is False
|
||||
|
||||
|
||||
# --- accepted configurations ---
|
||||
|
||||
@@ -15,6 +15,7 @@ from esphome.const import (
|
||||
KEY_CORE,
|
||||
KEY_TARGET_FRAMEWORK,
|
||||
KEY_TARGET_PLATFORM,
|
||||
PLATFORM_BK72XX,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_LN882X,
|
||||
PLATFORM_RP2,
|
||||
@@ -27,18 +28,20 @@ from ..types import SetCoreConfigCallable
|
||||
# Advertisement-only hub platforms; rp2 runs the full proxy and has its own
|
||||
# tests below.
|
||||
HUB_PLATFORM_FRAMEWORKS = [
|
||||
PlatformFramework.BK72XX_ARDUINO,
|
||||
PlatformFramework.LN882X_ARDUINO,
|
||||
]
|
||||
|
||||
HUB_TRACKERS = {
|
||||
PLATFORM_BK72XX: "bk72xx_ble_tracker",
|
||||
PLATFORM_LN882X: "ln882h_ble_tracker",
|
||||
PLATFORM_RP2: "rp2_ble_tracker",
|
||||
}
|
||||
|
||||
|
||||
def test_hub_platform_list_covers_every_hub_platform() -> None:
|
||||
# A platform added to _HUB_PLATFORMS (bk72xx is planned) would otherwise
|
||||
# get no gate coverage at all; GATT platforms have their own tests.
|
||||
# A platform added to _HUB_PLATFORMS would otherwise get no gate coverage
|
||||
# at all; GATT platforms have their own tests.
|
||||
advertisement_only = set(bluetooth_proxy._HUB_PLATFORMS) - set(
|
||||
bluetooth_connection.HUB_MAX_CONNECTIONS
|
||||
)
|
||||
|
||||
@@ -1,13 +1,31 @@
|
||||
"""Tests for the ethernet final-validation coexistence gate."""
|
||||
"""Tests for the ethernet final-validation coexistence gate and schema bounds."""
|
||||
|
||||
import pytest
|
||||
from voluptuous import Invalid
|
||||
|
||||
from esphome.components.ethernet import _final_validate
|
||||
from esphome import config_validation as cv
|
||||
from esphome.components.esp32 import (
|
||||
KEY_BOARD,
|
||||
KEY_IDF_VERSION,
|
||||
KEY_VARIANT,
|
||||
VARIANT_ESP32S3,
|
||||
)
|
||||
from esphome.components.ethernet import CONF_CLOCK_SPEED, CONFIG_SCHEMA, _final_validate
|
||||
from esphome.components.network import _validate_priority_list
|
||||
from esphome.const import CONF_PRIORITY
|
||||
from esphome.const import CONF_PRIORITY, PlatformFramework
|
||||
from esphome.core import CORE
|
||||
import esphome.final_validate as fv
|
||||
|
||||
from ..types import SetCoreConfigCallable
|
||||
|
||||
_CH390_CONFIG = {
|
||||
"type": "CH390",
|
||||
"clk_pin": 47,
|
||||
"mosi_pin": 48,
|
||||
"miso_pin": 14,
|
||||
"cs_pin": 21,
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_full_config():
|
||||
@@ -35,3 +53,40 @@ def test_rejects_wifi_and_ethernet_with_incomplete_priority() -> None:
|
||||
)
|
||||
with pytest.raises(Invalid, match=r"must.*list both interfaces; missing: wifi"):
|
||||
_final_validate({})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("clock_speed", ["26.67MHz", "72MHz"])
|
||||
def test_ch390_accepts_clock_speed_up_to_the_datasheet_maximum(
|
||||
set_core_config: SetCoreConfigCallable, clock_speed: str
|
||||
) -> None:
|
||||
"""CH390 SCK is rated to 72MHz, so the schema must accept the whole range."""
|
||||
set_core_config(
|
||||
PlatformFramework.ESP32_IDF,
|
||||
platform_data={
|
||||
KEY_BOARD: "esp32-s3-devkitc-1",
|
||||
KEY_VARIANT: VARIANT_ESP32S3,
|
||||
KEY_IDF_VERSION: cv.Version(5, 3, 2),
|
||||
},
|
||||
)
|
||||
# _validate derives use_address from the node name, which has no default here.
|
||||
CORE.name = "ch390-test"
|
||||
config = CONFIG_SCHEMA({**_CH390_CONFIG, CONF_CLOCK_SPEED: clock_speed})
|
||||
assert config[CONF_CLOCK_SPEED] == cv.frequency(clock_speed)
|
||||
|
||||
|
||||
def test_ch390_rejects_clock_speed_above_the_datasheet_maximum(
|
||||
set_core_config: SetCoreConfigCallable,
|
||||
) -> None:
|
||||
"""The shared 80MHz ceiling is out of spec for this part."""
|
||||
set_core_config(
|
||||
PlatformFramework.ESP32_IDF,
|
||||
platform_data={
|
||||
KEY_BOARD: "esp32-s3-devkitc-1",
|
||||
KEY_VARIANT: VARIANT_ESP32S3,
|
||||
KEY_IDF_VERSION: cv.Version(5, 3, 2),
|
||||
},
|
||||
)
|
||||
# _validate derives use_address from the node name, which has no default here.
|
||||
CORE.name = "ch390-test"
|
||||
with pytest.raises(Invalid, match="value must be at most 72000000"):
|
||||
CONFIG_SCHEMA({**_CH390_CONFIG, CONF_CLOCK_SPEED: "80MHz"})
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
mitsubishi_cn105:
|
||||
id: ac_hub
|
||||
|
||||
climate:
|
||||
- platform: mitsubishi_cn105
|
||||
mitsubishi_cn105_id: ac_hub
|
||||
name: AC
|
||||
current_temperature_min_interval: 30s
|
||||
uart_id: uart_bus
|
||||
update_interval: 10s
|
||||
@@ -0,0 +1,30 @@
|
||||
"""Tests for Mitsubishi CN105 climate configuration migration diagnostics."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.components.mitsubishi_cn105 import climate
|
||||
import esphome.config_validation as cv
|
||||
from esphome.core import CORE
|
||||
from esphome.yaml_util import load_yaml
|
||||
|
||||
|
||||
def test_top_level_hub_rejects_leftover_legacy_climate_keys(
|
||||
component_fixture_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
config = load_yaml(
|
||||
component_fixture_path("top_level_hub_with_legacy_climate_keys.yaml")
|
||||
)
|
||||
CORE.raw_config = config
|
||||
|
||||
with pytest.raises(cv.Invalid) as exc_info:
|
||||
climate.CONFIG_SCHEMA(config["climate"][0])
|
||||
|
||||
message = str(exc_info.value)
|
||||
assert "'current_temperature_min_interval'" in message
|
||||
assert "'uart_id'" in message
|
||||
assert "'update_interval'" in message
|
||||
assert "top-level 'mitsubishi_cn105:' block" in message
|
||||
assert "'telemetry_request_min_interval'" in message
|
||||
@@ -0,0 +1,5 @@
|
||||
esphome:
|
||||
name: preftest
|
||||
|
||||
bk72xx:
|
||||
board: generic-bk7252
|
||||
@@ -0,0 +1,5 @@
|
||||
esphome:
|
||||
name: preftest
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
@@ -0,0 +1,5 @@
|
||||
esphome:
|
||||
name: preftest
|
||||
|
||||
esp8266:
|
||||
board: esp01_1m
|
||||
@@ -0,0 +1,4 @@
|
||||
esphome:
|
||||
name: preftest
|
||||
|
||||
host:
|
||||
@@ -0,0 +1,6 @@
|
||||
esphome:
|
||||
name: preftest
|
||||
|
||||
nrf52:
|
||||
board: adafruit_itsybitsy_nrf52840
|
||||
bootloader: adafruit_nrf52_sd140_v6
|
||||
@@ -0,0 +1,5 @@
|
||||
esphome:
|
||||
name: preftest
|
||||
|
||||
rp2:
|
||||
board: rpipicow
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Every preferences platform either emits USE_PREFERENCE_KEY_LOOKUP from
|
||||
codegen (key-lookup backends) or must not (slot-based backends, whose managers
|
||||
have no load_from_key()). Run each platform's real codegen and assert the
|
||||
emission, mirroring the split the deny-list in esphome/core/defines.h assumes
|
||||
for static analysis.
|
||||
|
||||
The fixtures cover every distinct preferences backend today: ln882x and
|
||||
rtl87xx route through libretiny (bk72xx stands in for the family), rp2040 is
|
||||
an alias of rp2, and nrf52 exercises zephyr. A seventh backend needs a new
|
||||
fixture here."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("fixture", "emits"),
|
||||
[
|
||||
("esp32.yaml", True),
|
||||
("bk72xx.yaml", True), # libretiny
|
||||
("host.yaml", True),
|
||||
("nrf52.yaml", True), # zephyr
|
||||
("esp8266.yaml", False),
|
||||
("rp2.yaml", False),
|
||||
],
|
||||
)
|
||||
def test_key_lookup_define_matches_the_platform_backend(
|
||||
fixture: str,
|
||||
emits: bool,
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
generate_main(component_config_path(fixture))
|
||||
defines = {define.name for define in CORE.defines}
|
||||
assert ("USE_PREFERENCE_KEY_LOOKUP" in defines) is emits
|
||||
@@ -33,14 +33,49 @@ def test_web_server_auth_explicit_basic_no_warning(
|
||||
generate_main: Callable[[str], str],
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Auth type basic builds Basic and does not warn."""
|
||||
generate_main("tests/component_tests/web_server/web_server_auth_basic.yaml")
|
||||
"""Auth type basic on ESP32 uses plaintext credentials and does not warn."""
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/web_server/web_server_auth_basic.yaml"
|
||||
)
|
||||
|
||||
assert '->set_auth_username("admin");' in main_cpp
|
||||
assert '->set_auth_password("password");' in main_cpp
|
||||
assert "set_auth_basic_hash" not in main_cpp
|
||||
assert _has_define("USE_WEBSERVER_AUTH")
|
||||
assert not _has_define("USE_WEBSERVER_AUTH_DIGEST")
|
||||
assert _DEFAULT_CHANGE_WARNING not in caplog.text
|
||||
|
||||
|
||||
def test_web_server_auth_basic_esp8266_uses_precomputed_hash(
|
||||
generate_main: Callable[[str], str],
|
||||
) -> None:
|
||||
"""Auth type basic on ESP8266 emits the precomputed base64 hash, not the credentials."""
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/web_server/web_server_auth_basic_esp8266.yaml"
|
||||
)
|
||||
|
||||
assert '->set_auth_basic_hash("YWRtaW46cGFzc3dvcmQ=");' in main_cpp
|
||||
assert "set_auth_username" not in main_cpp
|
||||
assert "set_auth_password" not in main_cpp
|
||||
assert _has_define("USE_WEBSERVER_AUTH")
|
||||
assert not _has_define("USE_WEBSERVER_AUTH_DIGEST")
|
||||
|
||||
|
||||
def test_web_server_auth_digest_esp8266_uses_plaintext_credentials(
|
||||
generate_main: Callable[[str], str],
|
||||
) -> None:
|
||||
"""Auth type digest on ESP8266 uses plaintext credentials, not the basic hash."""
|
||||
main_cpp = generate_main(
|
||||
"tests/component_tests/web_server/web_server_auth_digest_esp8266.yaml"
|
||||
)
|
||||
|
||||
assert '->set_auth_username("admin");' in main_cpp
|
||||
assert '->set_auth_password("password");' in main_cpp
|
||||
assert "set_auth_basic_hash" not in main_cpp
|
||||
assert _has_define("USE_WEBSERVER_AUTH")
|
||||
assert _has_define("USE_WEBSERVER_AUTH_DIGEST")
|
||||
|
||||
|
||||
def test_web_server_auth_explicit_digest(
|
||||
generate_main: Callable[[str], str],
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp8266:
|
||||
board: esp01_1m
|
||||
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
|
||||
web_server:
|
||||
auth:
|
||||
username: admin
|
||||
password: password
|
||||
type: basic
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp8266:
|
||||
board: esp01_1m
|
||||
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
|
||||
web_server:
|
||||
auth:
|
||||
username: admin
|
||||
password: password
|
||||
type: digest
|
||||
Reference in New Issue
Block a user