From 49bbceb1dad7be50364ad5db11d4796df0061d59 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Jul 2026 13:51:54 -1000 Subject: [PATCH] [libretiny] Keep renamed board generic-ln882hki validating against generic-ln882h (#17542) --- esphome/components/libretiny/__init__.py | 15 ++++++ tests/unit_tests/components/test_libretiny.py | 52 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 tests/unit_tests/components/test_libretiny.py diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 3fde11b1ebf..62cef331fd8 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -76,12 +76,27 @@ _BLE5_BK_SYS_CONFIG_OPTIONS = [ "CFG_SUPPORT_BLE=0", ] +# Board ids upstream LibreTiny renamed; configs written against the old id +# keep validating and building against the new one (with a warning). +# generic-ln882hki -> generic-ln882h: LibreTiny v1.13.0. +_RENAMED_BOARDS = { + "generic-ln882hki": "generic-ln882h", +} + def _detect_variant(value): if KEY_LIBRETINY not in CORE.data: raise cv.Invalid("Family component didn't populate core data properly!") component: LibreTinyComponent = CORE.data[KEY_LIBRETINY][KEY_COMPONENT_DATA] board = value[CONF_BOARD] + if board not in component.boards and (renamed := _RENAMED_BOARDS.get(board)): + _LOGGER.warning( + "Board '%s' was renamed to '%s'; please update your configuration", + board, + renamed, + ) + value = value.copy() + value[CONF_BOARD] = board = renamed # read board-default family if not specified if board not in component.boards: if CONF_FAMILY not in value: diff --git a/tests/unit_tests/components/test_libretiny.py b/tests/unit_tests/components/test_libretiny.py new file mode 100644 index 00000000000..ee00bdc1800 --- /dev/null +++ b/tests/unit_tests/components/test_libretiny.py @@ -0,0 +1,52 @@ +"""Tests for LibreTiny board detection, including renamed-board migration.""" + +import pytest + +from esphome.components.libretiny import _detect_variant +from esphome.components.libretiny.const import ( + FAMILY_LN882H, + KEY_COMPONENT_DATA, + KEY_LIBRETINY, +) +from esphome.components.ln882x import COMPONENT_DATA +import esphome.config_validation as cv +from esphome.const import CONF_BOARD, CONF_FAMILY +from esphome.core import CORE + + +@pytest.fixture +def ln882x_core_data() -> None: + """Populate CORE the way the ln882x component schema does.""" + CORE.data[KEY_LIBRETINY] = {KEY_COMPONENT_DATA: COMPONENT_DATA} + + +def test_detect_variant_known_board_passes(ln882x_core_data: None) -> None: + """A current board id resolves its family without warnings.""" + result = _detect_variant({CONF_BOARD: "generic-ln882h"}) + assert result[CONF_BOARD] == "generic-ln882h" + assert result[CONF_FAMILY] == FAMILY_LN882H + + +def test_detect_variant_renamed_board_migrates( + ln882x_core_data: None, caplog: pytest.LogCaptureFixture +) -> None: + """A pre-rename board id validates against the new id, with a warning.""" + result = _detect_variant({CONF_BOARD: "generic-ln882hki"}) + assert result[CONF_BOARD] == "generic-ln882h" + assert result[CONF_FAMILY] == FAMILY_LN882H + assert "renamed to 'generic-ln882h'" in caplog.text + + +def test_detect_variant_renamed_board_does_not_mutate_input( + ln882x_core_data: None, +) -> None: + """Migration copies the config; the caller's dict keeps the old id.""" + value = {CONF_BOARD: "generic-ln882hki"} + _detect_variant(value) + assert value[CONF_BOARD] == "generic-ln882hki" + + +def test_detect_variant_unknown_board_still_raises(ln882x_core_data: None) -> None: + """Ids outside the rename map keep the family-override error.""" + with pytest.raises(cv.Invalid, match="This board is unknown"): + _detect_variant({CONF_BOARD: "not-a-real-board"})