diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index bc26db9956..58915ff96d 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -import logging from esphome import automation, pins from esphome.automation import Condition @@ -62,7 +61,6 @@ from esphome.types import ConfigType AUTO_LOAD = ["network"] DOMAIN = "ethernet" -LOGGER = logging.getLogger(__name__) # Key for tracking IP state listener count in CORE.data ETHERNET_IP_STATE_LISTENERS_KEY = "ethernet_ip_state_listeners" @@ -214,13 +212,6 @@ CLK_MODES = { "CLK_OUT": emac_rmii_clock_mode_t.EMAC_CLK_OUT, } -CLK_MODES_DEPRECATED = { - "GPIO0_IN": ("CLK_EXT_IN", 0), - "GPIO0_OUT": ("CLK_OUT", 0), - "GPIO16_OUT": ("CLK_OUT", 16), - "GPIO17_OUT": ("CLK_OUT", 17), -} - spi_host_device_t = cg.global_ns.enum("spi_host_device_t") SPI_INTERFACE_MAP = { @@ -387,23 +378,6 @@ def _validate(config: ConfigType) -> ConfigType: get_esp32_variant, ) - if CONF_CLK_MODE in config: - mode, pin = CLK_MODES_DEPRECATED[config[CONF_CLK_MODE]] - LOGGER.warning( - "[ethernet] The 'clk_mode' option is deprecated. " - "Please replace 'clk_mode: %s' with:\n" - " clk:\n" - " mode: %s\n" - " pin: %s\n" - "Removal scheduled for 2026.11.0.", - config[CONF_CLK_MODE], - mode, - pin, - ) - config[CONF_CLK] = CLK_SCHEMA({CONF_MODE: mode, CONF_PIN: pin}) - del config[CONF_CLK_MODE] - elif CONF_CLK not in config: - raise cv.Invalid("'clk' is a required option for [ethernet].") variant = get_esp32_variant() if variant not in (VARIANT_ESP32, VARIANT_ESP32P4): raise cv.Invalid( @@ -452,10 +426,17 @@ RMII_SCHEMA = cv.All( { cv.Required(CONF_MDC_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_MDIO_PIN): pins.internal_gpio_output_pin_number, - cv.Optional(CONF_CLK_MODE): cv.enum( - CLK_MODES_DEPRECATED, upper=True, space="_" + # Removed in 2026.11.0 - kept to provide helpful error message + # Remove before 2027.5.0 + cv.Optional(CONF_CLK_MODE): cv.invalid( + "The 'clk_mode' option has been removed in ESPHome 2026.11.0.\n" + "Replace it with a 'clk:' block containing 'mode:' and 'pin:':\n" + " GPIO0_IN -> mode: CLK_EXT_IN, pin: GPIO0\n" + " GPIO0_OUT -> mode: CLK_OUT, pin: GPIO0\n" + " GPIO16_OUT -> mode: CLK_OUT, pin: GPIO16\n" + " GPIO17_OUT -> mode: CLK_OUT, pin: GPIO17" ), - cv.Optional(CONF_CLK): CLK_SCHEMA, + cv.Required(CONF_CLK): CLK_SCHEMA, cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), cv.Optional(CONF_POWER_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_PHY_REGISTERS): cv.ensure_list(PHY_REGISTER_SCHEMA), diff --git a/tests/benchmarks/python/fixtures/bluetooth_proxy_device.yaml b/tests/benchmarks/python/fixtures/bluetooth_proxy_device.yaml index dfa5a487b8..c74846689b 100644 --- a/tests/benchmarks/python/fixtures/bluetooth_proxy_device.yaml +++ b/tests/benchmarks/python/fixtures/bluetooth_proxy_device.yaml @@ -25,7 +25,9 @@ ethernet: type: LAN8720 mdc_pin: GPIO23 mdio_pin: GPIO18 - clk_mode: GPIO17_OUT + clk: + mode: CLK_OUT + pin: GPIO17 phy_addr: 0 power_pin: GPIO12 diff --git a/tests/component_tests/ethernet/test_ethernet.py b/tests/component_tests/ethernet/test_ethernet.py index 522357f972..73985597d9 100644 --- a/tests/component_tests/ethernet/test_ethernet.py +++ b/tests/component_tests/ethernet/test_ethernet.py @@ -8,6 +8,7 @@ from esphome.components.esp32 import ( KEY_BOARD, KEY_IDF_VERSION, KEY_VARIANT, + VARIANT_ESP32, VARIANT_ESP32S3, ) from esphome.components.ethernet import CONF_CLOCK_SPEED, CONFIG_SCHEMA, _final_validate @@ -82,3 +83,40 @@ def test_ch390_rejects_clock_speed_above_the_datasheet_maximum( CORE.name = "ch390-test" with pytest.raises(Invalid, match="value must be at most 72000000"): CONFIG_SCHEMA({**_CH390_CONFIG, CONF_CLOCK_SPEED: "80MHz"}) + + +_LAN8720_CONFIG = {"type": "LAN8720", "mdc_pin": 23, "mdio_pin": 18} + + +def _set_esp32_core(set_core_config: SetCoreConfigCallable) -> None: + set_core_config( + PlatformFramework.ESP32_IDF, + platform_data={ + KEY_BOARD: "esp32dev", + KEY_VARIANT: VARIANT_ESP32, + KEY_IDF_VERSION: cv.Version(5, 3, 2), + }, + ) + CORE.name = "rmii-test" + + +def test_rmii_rejects_removed_clk_mode( + set_core_config: SetCoreConfigCallable, +) -> None: + """The removed clk_mode option points at the clk: replacement.""" + _set_esp32_core(set_core_config) + with pytest.raises(Invalid, match="GPIO17_OUT -> mode: CLK_OUT, pin: GPIO17"): + CONFIG_SCHEMA( + { + **_LAN8720_CONFIG, + "clk_mode": "GPIO17_OUT", + "clk": {"mode": "CLK_OUT", "pin": 17}, + } + ) + + +def test_rmii_requires_clk(set_core_config: SetCoreConfigCallable) -> None: + """An RMII PHY has no default clock, so clk must be given.""" + _set_esp32_core(set_core_config) + with pytest.raises(Invalid, match=r"required key not provided @ data\['clk'\]"): + CONFIG_SCHEMA(_LAN8720_CONFIG)