mirror of
https://github.com/esphome/esphome.git
synced 2026-08-23 06:36:23 +00:00
[esp32] Add RTC-backed preferences (honor in_flash flag) (#17073)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
2c24e82ba3
commit
8ab19c0242
@@ -0,0 +1,4 @@
|
||||
# Exercises the opt-in that compiles the RTC-backed preference storage into
|
||||
# the ESP32 backend without any other option selecting it.
|
||||
preferences:
|
||||
rtc_storage: true
|
||||
@@ -0,0 +1,4 @@
|
||||
# Exercises the ESP32 RTC-backed preferences path (storage: rtc) for safe_mode.
|
||||
safe_mode:
|
||||
num_attempts: 3
|
||||
storage: rtc
|
||||
@@ -0,0 +1,7 @@
|
||||
# Exercises the dict form of fast_connect with RTC-backed preference storage.
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
fast_connect:
|
||||
enabled: true
|
||||
storage: rtc
|
||||
@@ -0,0 +1,7 @@
|
||||
# Exercises the dict form of fast_connect overriding the ESP8266 default (rtc)
|
||||
# back to flash storage.
|
||||
wifi:
|
||||
ssid: MySSID
|
||||
password: password1
|
||||
fast_connect:
|
||||
storage: flash
|
||||
@@ -0,0 +1,149 @@
|
||||
"""Tests for esphome.preferences storage backend selection."""
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome import preferences
|
||||
from esphome.components.esp32 import KEY_ESP32
|
||||
from esphome.components.esp32.const import (
|
||||
VARIANT_ESP32,
|
||||
VARIANT_ESP32C2,
|
||||
VARIANT_ESP32C3,
|
||||
VARIANT_ESP32C61,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_STORAGE,
|
||||
KEY_CORE,
|
||||
KEY_TARGET_PLATFORM,
|
||||
KEY_VARIANT,
|
||||
PLATFORM_ESP32,
|
||||
PLATFORM_ESP8266,
|
||||
PLATFORM_RP2040,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
def _set_platform(platform: str) -> None:
|
||||
CORE.data[KEY_CORE] = {KEY_TARGET_PLATFORM: platform}
|
||||
|
||||
|
||||
def _set_esp32(variant: str) -> None:
|
||||
_set_platform(PLATFORM_ESP32)
|
||||
CORE.data[KEY_ESP32] = {KEY_VARIANT: variant}
|
||||
|
||||
|
||||
def _validate(value: dict):
|
||||
return cv.Schema(preferences.storage_schema())(value)
|
||||
|
||||
|
||||
def _define_names() -> set[str]:
|
||||
return {define.name for define in CORE.defines}
|
||||
|
||||
|
||||
def test_is_in_flash() -> None:
|
||||
_set_platform(PLATFORM_ESP8266)
|
||||
assert preferences.is_in_flash(preferences.STORAGE_FLASH) is True
|
||||
assert preferences.is_in_flash(preferences.STORAGE_RTC) is False
|
||||
# The RTC storage define is ESP32-specific.
|
||||
assert "USE_ESP32_RTC_PREFERENCES" not in _define_names()
|
||||
|
||||
|
||||
def test_is_in_flash_esp32_rtc_emits_define() -> None:
|
||||
_set_esp32(VARIANT_ESP32)
|
||||
assert preferences.is_in_flash(preferences.STORAGE_FLASH) is True
|
||||
assert "USE_ESP32_RTC_PREFERENCES" not in _define_names()
|
||||
assert preferences.is_in_flash(preferences.STORAGE_RTC) is False
|
||||
assert "USE_ESP32_RTC_PREFERENCES" in _define_names()
|
||||
|
||||
|
||||
def test_request_rtc_storage_esp32_only() -> None:
|
||||
_set_platform(PLATFORM_ESP8266)
|
||||
preferences.request_rtc_storage()
|
||||
# ESP8266 always has its RTC backend; no define is needed or emitted.
|
||||
assert "USE_ESP32_RTC_PREFERENCES" not in _define_names()
|
||||
|
||||
|
||||
def test_request_rtc_storage_esp32_emits_define() -> None:
|
||||
_set_esp32(VARIANT_ESP32)
|
||||
preferences.request_rtc_storage()
|
||||
assert "USE_ESP32_RTC_PREFERENCES" in _define_names()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("variant", [VARIANT_ESP32, VARIANT_ESP32C3])
|
||||
def test_validate_rtc_storage_accepted(variant: str) -> None:
|
||||
_set_esp32(variant)
|
||||
assert preferences.validate_rtc_storage(True) is True
|
||||
assert preferences.validate_rtc_storage(False) is False
|
||||
|
||||
|
||||
def test_validate_rtc_storage_esp8266() -> None:
|
||||
_set_platform(PLATFORM_ESP8266)
|
||||
# Tolerated no-op: the ESP8266 backend always has RTC storage.
|
||||
assert preferences.validate_rtc_storage(True) is True
|
||||
# But it cannot be disabled, so an explicit false is an error.
|
||||
with pytest.raises(cv.Invalid, match="always enabled on ESP8266"):
|
||||
preferences.validate_rtc_storage(False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("variant", [VARIANT_ESP32C2, VARIANT_ESP32C61])
|
||||
def test_validate_rtc_storage_rejected_without_rtc_memory(variant: str) -> None:
|
||||
_set_esp32(variant)
|
||||
with pytest.raises(cv.Invalid, match="not supported on this platform"):
|
||||
preferences.validate_rtc_storage(True)
|
||||
# Disabling it is always fine.
|
||||
assert preferences.validate_rtc_storage(False) is False
|
||||
|
||||
|
||||
def test_validate_rtc_storage_rejected_on_unsupported_platform() -> None:
|
||||
_set_platform(PLATFORM_RP2040)
|
||||
with pytest.raises(cv.Invalid, match="not supported on this platform"):
|
||||
preferences.validate_rtc_storage(True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("platform", "expected"),
|
||||
[
|
||||
# Defaults preserve each platform's historic behavior.
|
||||
(PLATFORM_ESP8266, preferences.STORAGE_RTC),
|
||||
(PLATFORM_RP2040, preferences.STORAGE_FLASH),
|
||||
],
|
||||
)
|
||||
def test_default_storage_per_platform(platform: str, expected: str) -> None:
|
||||
_set_platform(platform)
|
||||
assert _validate({})[CONF_STORAGE] == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("variant", [VARIANT_ESP32, VARIANT_ESP32C2])
|
||||
def test_default_storage_esp32_is_flash(variant: str) -> None:
|
||||
# ESP32 defaults to flash on every variant, including those without RTC memory.
|
||||
_set_esp32(variant)
|
||||
assert _validate({})[CONF_STORAGE] == preferences.STORAGE_FLASH
|
||||
|
||||
|
||||
def test_rtc_allowed_on_esp8266() -> None:
|
||||
_set_platform(PLATFORM_ESP8266)
|
||||
assert _validate({CONF_STORAGE: "rtc"})[CONF_STORAGE] == preferences.STORAGE_RTC
|
||||
|
||||
|
||||
@pytest.mark.parametrize("variant", [VARIANT_ESP32, VARIANT_ESP32C3])
|
||||
def test_rtc_allowed_on_esp32_with_rtc_memory(variant: str) -> None:
|
||||
_set_esp32(variant)
|
||||
assert _validate({CONF_STORAGE: "rtc"})[CONF_STORAGE] == preferences.STORAGE_RTC
|
||||
|
||||
|
||||
@pytest.mark.parametrize("variant", [VARIANT_ESP32C2, VARIANT_ESP32C61])
|
||||
def test_rtc_rejected_on_esp32_without_rtc_memory(variant: str) -> None:
|
||||
_set_esp32(variant)
|
||||
with pytest.raises(cv.Invalid, match="not supported on this platform"):
|
||||
_validate({CONF_STORAGE: "rtc"})
|
||||
|
||||
|
||||
def test_rtc_rejected_on_unsupported_platform() -> None:
|
||||
_set_platform(PLATFORM_RP2040)
|
||||
with pytest.raises(cv.Invalid, match="not supported on this platform"):
|
||||
_validate({CONF_STORAGE: "rtc"})
|
||||
|
||||
|
||||
def test_flash_allowed_on_unsupported_platform() -> None:
|
||||
_set_platform(PLATFORM_RP2040)
|
||||
assert _validate({CONF_STORAGE: "flash"})[CONF_STORAGE] == preferences.STORAGE_FLASH
|
||||
Reference in New Issue
Block a user