[core] Expose the preferences storage helpers to components (#20535)

This commit is contained in:
J. Nick Koston
2026-10-11 20:42:21 -04:00
committed by GitHub
parent 22c17fe7e1
commit e50a0d902e
2 changed files with 52 additions and 10 deletions
+8 -10
View File
@@ -19,18 +19,15 @@ STORAGE_FLASH = "flash"
STORAGE_RTC = "rtc"
def _rtc_supported() -> bool:
"""Whether the active platform has an RTC-backed preferences backend.
Mirrors the C++ ``SOC_RTC_MEM_SUPPORTED`` guard in the ESP32 backend: the ESP32-C2
and -C61 have no RTC memory at all, so RTC storage is unavailable there.
"""
def rtc_supported() -> bool:
"""Whether the target has RTC-backed preferences (not ESP32-C2/C61); call at validation time."""
if CORE.is_esp8266:
return True
if CORE.is_esp32:
from esphome.components.esp32 import get_esp32_variant
from esphome.components.esp32.const import VARIANT_ESP32C2, VARIANT_ESP32C61
# Mirrors the C++ SOC_RTC_MEM_SUPPORTED guard in the ESP32 backend
return get_esp32_variant() not in (VARIANT_ESP32C2, VARIANT_ESP32C61)
return False
@@ -44,9 +41,10 @@ def _default_storage() -> str:
return STORAGE_RTC if CORE.is_esp8266 else STORAGE_FLASH
def _validate_storage(value):
def validate_storage(value: object) -> str:
"""Validate a ``storage:`` value, ``flash`` or ``rtc`` where supported."""
value = cv.one_of(STORAGE_FLASH, STORAGE_RTC, lower=True)(value)
if value == STORAGE_RTC and not _rtc_supported():
if value == STORAGE_RTC and not rtc_supported():
raise cv.Invalid(
f"'{STORAGE_RTC}' storage is not supported on this platform; only "
f"'{STORAGE_FLASH}' is available"
@@ -56,7 +54,7 @@ def _validate_storage(value):
def storage_schema():
"""Return an Optional(CONF_STORAGE) entry for merging into a component schema."""
return {cv.Optional(CONF_STORAGE, default=_default_storage): _validate_storage}
return {cv.Optional(CONF_STORAGE, default=_default_storage): validate_storage}
def request_rtc_storage() -> None:
@@ -88,7 +86,7 @@ def validate_rtc_storage(value):
"be disabled"
)
return value
if not _rtc_supported():
if not rtc_supported():
raise cv.Invalid("RTC preference storage is not supported on this platform")
return value
+44
View File
@@ -147,3 +147,47 @@ def test_rtc_rejected_on_unsupported_platform() -> None:
def test_flash_allowed_on_unsupported_platform() -> None:
_set_platform(PLATFORM_RP2040)
assert _validate({CONF_STORAGE: "flash"})[CONF_STORAGE] == preferences.STORAGE_FLASH
@pytest.mark.parametrize("variant", [VARIANT_ESP32, VARIANT_ESP32C3])
def test_rtc_supported_on_esp32_with_rtc_memory(variant: str) -> None:
_set_esp32(variant)
assert preferences.rtc_supported() is True
@pytest.mark.parametrize("variant", [VARIANT_ESP32C2, VARIANT_ESP32C61])
def test_rtc_supported_false_without_rtc_memory(variant: str) -> None:
_set_esp32(variant)
assert preferences.rtc_supported() is False
@pytest.mark.parametrize(
("platform", "expected"),
[(PLATFORM_ESP8266, True), (PLATFORM_RP2040, False)],
)
def test_rtc_supported_per_platform(platform: str, expected: bool) -> None:
_set_platform(platform)
assert preferences.rtc_supported() is expected
def test_validate_storage_accepts_flash_everywhere() -> None:
_set_platform(PLATFORM_RP2040)
assert preferences.validate_storage("flash") == preferences.STORAGE_FLASH
assert preferences.validate_storage("FLASH") == preferences.STORAGE_FLASH
def test_validate_storage_accepts_rtc_where_supported() -> None:
_set_esp32(VARIANT_ESP32)
assert preferences.validate_storage("rtc") == preferences.STORAGE_RTC
def test_validate_storage_rejects_rtc_without_rtc_memory() -> None:
_set_esp32(VARIANT_ESP32C2)
with pytest.raises(cv.Invalid, match="not supported on this platform"):
preferences.validate_storage("rtc")
def test_validate_storage_rejects_unknown_value() -> None:
_set_esp32(VARIANT_ESP32)
with pytest.raises(cv.Invalid):
preferences.validate_storage("nvs")