mirror of
https://github.com/esphome/esphome.git
synced 2026-08-23 06:36:23 +00:00
107 lines
3.9 KiB
Python
107 lines
3.9 KiB
Python
"""Helpers for letting a component choose where a preference is persisted.
|
|
|
|
Preferences can be stored either in flash (durable across power loss) or in RTC
|
|
memory (fast, survives deep sleep and soft resets but not power loss). The
|
|
flash-vs-RTC choice is only meaningful on platforms whose preferences backend
|
|
honors the ``in_flash`` flag — currently ESP32 and ESP8266. On other platforms
|
|
the value is accepted only as ``flash`` (the sole supported backend).
|
|
|
|
Components include :func:`storage_schema` in their config and convert the chosen
|
|
value with :func:`is_in_flash` when calling ``make_preference``.
|
|
"""
|
|
|
|
import esphome.codegen as cg
|
|
import esphome.config_validation as cv
|
|
from esphome.const import CONF_STORAGE
|
|
from esphome.core import CORE
|
|
|
|
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.
|
|
"""
|
|
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
|
|
|
|
return get_esp32_variant() not in (VARIANT_ESP32C2, VARIANT_ESP32C61)
|
|
return False
|
|
|
|
|
|
def _default_storage() -> str:
|
|
"""Default that preserves each platform's historic behavior.
|
|
|
|
ESP8266 has always stored these preferences in RTC memory; every other
|
|
platform effectively used flash. Evaluated at validation time.
|
|
"""
|
|
return STORAGE_RTC if CORE.is_esp8266 else STORAGE_FLASH
|
|
|
|
|
|
def _validate_storage(value):
|
|
value = cv.one_of(STORAGE_FLASH, STORAGE_RTC, lower=True)(value)
|
|
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"
|
|
)
|
|
return 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}
|
|
|
|
|
|
def request_rtc_storage() -> None:
|
|
"""Compile the RTC-backed storage into the ESP32 preferences backend.
|
|
|
|
The RTC storage region is left out of ESP32 builds unless something asks for
|
|
it, so unused builds don't reserve RTC memory. Call this from ``to_code``
|
|
when a config option selects RTC storage. No-op on other platforms (ESP8266
|
|
always has its RTC backend).
|
|
"""
|
|
if CORE.is_esp32:
|
|
cg.add_define("USE_ESP32_RTC_PREFERENCES")
|
|
|
|
|
|
def validate_rtc_storage(value):
|
|
"""Validate a boolean option that requests RTC-backed preference storage.
|
|
|
|
``false`` means "no request", not "disable": it never turns RTC storage off
|
|
(another option selecting ``storage: rtc`` still compiles it in). On ESP8266
|
|
the backend is integral and always enabled, so ``false`` is rejected rather
|
|
than silently ignored; ``true`` is a tolerated no-op there so shared config
|
|
packages work across mixed fleets.
|
|
"""
|
|
value = cv.boolean(value)
|
|
if not value:
|
|
if CORE.is_esp8266:
|
|
raise cv.Invalid(
|
|
"RTC preference storage is always enabled on ESP8266 and cannot "
|
|
"be disabled"
|
|
)
|
|
return value
|
|
if not _rtc_supported():
|
|
raise cv.Invalid("RTC preference storage is not supported on this platform")
|
|
return value
|
|
|
|
|
|
def is_in_flash(value: str) -> bool:
|
|
"""Map a CONF_STORAGE value to the ``in_flash`` argument of make_preference.
|
|
|
|
Call this from ``to_code``: when RTC storage is selected on ESP32 it also emits
|
|
the define that compiles the RTC storage buffer into the ESP32 backend (see
|
|
:func:`request_rtc_storage`).
|
|
"""
|
|
in_flash = value == STORAGE_FLASH
|
|
if not in_flash:
|
|
request_rtc_storage()
|
|
return in_flash
|