mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 18:22:48 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70fa11d846 | ||
|
|
f60c662a5b | ||
|
|
5893f4565d | ||
|
|
0987f14f70 | ||
|
|
66dfaa6d2a | ||
|
|
44e962ab96 | ||
|
|
eb7a741c24 | ||
|
|
4767efcc29 | ||
|
|
34d251fe7e |
@@ -53,10 +53,14 @@ from .const import (
|
||||
KEY_SERIAL1_REQUIRED,
|
||||
KEY_SERIAL_REQUIRED,
|
||||
KEY_WAVEFORM_REQUIRED,
|
||||
PREFERENCES_RTC_EBOOT_WORDS,
|
||||
PREFERENCES_RTC_NORMAL_WORDS,
|
||||
THROW_STUBS_HEADER,
|
||||
check_preferences_fit,
|
||||
enable_serial,
|
||||
enable_serial1,
|
||||
esp8266_ns,
|
||||
preferences_flash_words,
|
||||
)
|
||||
from .gpio import PinInitialState, add_pin_initial_states_array
|
||||
|
||||
@@ -467,6 +471,12 @@ async def to_code(config: ConfigType) -> None:
|
||||
# Other projects like Tasmota & ESPEasy also use this
|
||||
cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH")
|
||||
|
||||
cg.add_define("ESP8266_PREFERENCES_RTC_NORMAL_WORDS", PREFERENCES_RTC_NORMAL_WORDS)
|
||||
cg.add_define("ESP8266_PREFERENCES_RTC_EBOOT_WORDS", PREFERENCES_RTC_EBOOT_WORDS)
|
||||
cg.add_define(
|
||||
"ESP8266_PREFERENCES_FLASH_WORDS",
|
||||
preferences_flash_words(config[CONF_RESTORE_FROM_FLASH]),
|
||||
)
|
||||
if config[CONF_RESTORE_FROM_FLASH]:
|
||||
cg.add_define("USE_ESP8266_PREFERENCES_FLASH")
|
||||
|
||||
@@ -528,6 +538,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
CORE.add_job(add_pin_initial_states_array)
|
||||
CORE.add_job(finalize_waveform_config)
|
||||
CORE.add_job(finalize_serial_config)
|
||||
CORE.add_job(check_preferences_fit)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.WORKAROUNDS)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import logging
|
||||
|
||||
import esphome.codegen as cg
|
||||
|
||||
# Re-exported from the shared definition; here it indexes the BOARDS
|
||||
# metadata dicts, whose entries in boards.py spell the literal.
|
||||
from esphome.const import KEY_FLASH_SIZE # noqa: F401 # pylint: disable=unused-import
|
||||
from esphome.core import CORE
|
||||
from esphome.core import CORE, CoroPriority, EsphomeError, coroutine_with_priority
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
KEY_ESP8266 = "esp8266"
|
||||
KEY_BOARD = "board"
|
||||
@@ -24,6 +28,87 @@ KEY_LDSCRIPT = "ldscript"
|
||||
# Crash-decode tools, resolved once per run by __init__
|
||||
KEY_DECODE_TOOLS = "decode_tools"
|
||||
|
||||
# Saved-settings space in 4-byte words; preferences.cpp gets these as defines. A value cannot span the two
|
||||
# RTC regions, and the flash store is smaller when only explicit flash preferences use it.
|
||||
PREFERENCES_RTC_NORMAL_WORDS = 78
|
||||
PREFERENCES_RTC_EBOOT_WORDS = 32
|
||||
PREFERENCES_RTC_WORDS = PREFERENCES_RTC_NORMAL_WORDS + PREFERENCES_RTC_EBOOT_WORDS
|
||||
PREFERENCES_FLASH_WORDS = 128
|
||||
PREFERENCES_FLASH_WORDS_WITHOUT_RESTORE = 64
|
||||
KEY_PREFERENCES = "preferences"
|
||||
|
||||
|
||||
def preferences_flash_words(restore_from_flash: bool) -> int:
|
||||
"""Size of the flash store for this configuration."""
|
||||
return (
|
||||
PREFERENCES_FLASH_WORDS
|
||||
if restore_from_flash
|
||||
else PREFERENCES_FLASH_WORDS_WITHOUT_RESTORE
|
||||
)
|
||||
|
||||
|
||||
def preference_words(value_bytes: int) -> int:
|
||||
"""Words a saved value takes, checksum word included (rtc_pref_bytes_to_words plus one)."""
|
||||
return (value_bytes + 3) // 4 + 1
|
||||
|
||||
|
||||
def reserve_preferences(
|
||||
count: int, value_bytes: int, in_flash: bool | None = None
|
||||
) -> None:
|
||||
"""Record saved values a component creates; check_preferences_fit() adds them all up.
|
||||
|
||||
in_flash is the value's own storage, as passed to make_preference(); None is the two-argument
|
||||
form, which follows restore_from_flash.
|
||||
"""
|
||||
if in_flash is None:
|
||||
in_flash = CORE.config[KEY_ESP8266][CONF_RESTORE_FROM_FLASH]
|
||||
reservations = CORE.data.setdefault(KEY_ESP8266, {}).setdefault(KEY_PREFERENCES, [])
|
||||
reservations.extend([(preference_words(value_bytes), in_flash)] * count)
|
||||
|
||||
|
||||
def _rtc_values_can_split(values: list[int]) -> bool:
|
||||
"""Whether some order fills the two RTC regions without a value spanning them."""
|
||||
total = sum(values)
|
||||
if total <= PREFERENCES_RTC_NORMAL_WORDS:
|
||||
return True
|
||||
# Bit i of reachable is set when some subset of the values sums to i
|
||||
reachable = 1
|
||||
for words in values:
|
||||
reachable |= reachable << words
|
||||
lowest = max(0, total - PREFERENCES_RTC_EBOOT_WORDS)
|
||||
highest = min(total, PREFERENCES_RTC_NORMAL_WORDS)
|
||||
return any(reachable >> normal & 1 for normal in range(lowest, highest + 1))
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.FINAL)
|
||||
async def check_preferences_fit() -> None:
|
||||
"""Fail the build when the reserved saved values cannot fit the space preferences.cpp has."""
|
||||
flash_store = preferences_flash_words(
|
||||
CORE.config[KEY_ESP8266][CONF_RESTORE_FROM_FLASH]
|
||||
)
|
||||
reservations = CORE.data[KEY_ESP8266].get(KEY_PREFERENCES, [])
|
||||
flash_words = sum(w for w, in_flash in reservations if in_flash)
|
||||
rtc_values = [w for w, in_flash in reservations if not in_flash]
|
||||
if flash_words > flash_store:
|
||||
raise EsphomeError(
|
||||
f"Saved values need {flash_words} words but the ESP8266 flash store has {flash_store}; "
|
||||
"use fewer or smaller saved values"
|
||||
)
|
||||
if not _rtc_values_can_split(rtc_values):
|
||||
raise EsphomeError(
|
||||
f"Saved values need {sum(rtc_values)} words but ESP8266 RTC memory has {PREFERENCES_RTC_WORDS} in "
|
||||
f"regions of {PREFERENCES_RTC_NORMAL_WORDS} and {PREFERENCES_RTC_EBOOT_WORDS} that a value cannot "
|
||||
"span; use fewer or smaller saved values, or store them in flash"
|
||||
)
|
||||
if sum(rtc_values) > PREFERENCES_RTC_NORMAL_WORDS:
|
||||
_LOGGER.warning(
|
||||
"Saved values reserve %d of %d words of ESP8266 RTC memory; the fit depends on setup order, and "
|
||||
"saved settings that do not reserve are not counted",
|
||||
sum(rtc_values),
|
||||
PREFERENCES_RTC_WORDS,
|
||||
)
|
||||
|
||||
|
||||
# esp8266 namespace is already defined by arduino, manually prefix esphome
|
||||
esp8266_ns = cg.global_ns.namespace("esphome").namespace("esp8266")
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#ifdef USE_ESP8266_CRASH_HANDLER
|
||||
|
||||
#include "crash_handler.h"
|
||||
#include "rtc_layout.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <cinttypes>
|
||||
@@ -46,11 +47,8 @@ static inline bool IRAM_ATTR is_code_addr(uint32_t val) {
|
||||
// Recover the actual code address from a windowed-ABI return address on the stack.
|
||||
static inline uint32_t IRAM_ATTR recover_code_addr(uint32_t val) { return (val & XTENSA_ADDR_MASK) | XTENSA_CODE_BASE; }
|
||||
|
||||
// RTC user memory layout for crash backtrace data.
|
||||
// User-accessible RTC memory: blocks 64-191 (each block = 4 bytes).
|
||||
// We use blocks 174-191 (last 18 blocks, 72 bytes) to minimize conflicts.
|
||||
// Crash data lives in the last esphome::esp8266::RTC_CRASH_WORDS of user RTC memory (rtc_layout.h).
|
||||
// Store 16 raw candidates, filter to real return addresses at log time.
|
||||
static constexpr uint8_t RTC_CRASH_BASE = 174;
|
||||
static constexpr size_t MAX_BACKTRACE = 16;
|
||||
|
||||
// Magic word packs sentinel, version, and count into one uint32_t:
|
||||
@@ -78,7 +76,7 @@ struct RtcCrashData {
|
||||
uint32_t backtrace[MAX_BACKTRACE];
|
||||
uint32_t epc1; // Fault PC, used to filter duplicates
|
||||
};
|
||||
static_assert(sizeof(RtcCrashData) == 72, "RtcCrashData must fit in 18 RTC blocks");
|
||||
static_assert(sizeof(RtcCrashData) == esphome::esp8266::RTC_CRASH_WORDS * 4, "RtcCrashData must fill its RTC words");
|
||||
|
||||
namespace esphome::esp8266 {
|
||||
|
||||
@@ -129,7 +127,7 @@ static const LogString *get_reset_reason(uint32_t reason) {
|
||||
// Returns the number of valid backtrace entries (0 if no data found).
|
||||
static uint8_t read_rtc_backtrace(uint32_t *backtrace, size_t max_entries) {
|
||||
RtcCrashData rtc_data;
|
||||
if (!system_rtc_mem_read(RTC_CRASH_BASE, &rtc_data, sizeof(rtc_data)))
|
||||
if (!system_rtc_mem_read(esphome::esp8266::RTC_CRASH_BASE, &rtc_data, sizeof(rtc_data)))
|
||||
return 0;
|
||||
uint32_t magic = rtc_data.magic;
|
||||
if ((magic & CRASH_SENTINEL_MASK) != CRASH_SENTINEL || (magic & CRASH_VERSION_MASK) != CRASH_VERSION)
|
||||
@@ -233,7 +231,7 @@ extern "C" void IRAM_ATTR custom_crash_callback(struct rst_info *rst_info, uint3
|
||||
data.epc1 = epc1;
|
||||
data.magic = CRASH_SENTINEL | CRASH_VERSION | count;
|
||||
|
||||
system_rtc_mem_write(RTC_CRASH_BASE, &data, sizeof(data));
|
||||
system_rtc_mem_write(esphome::esp8266::RTC_CRASH_BASE, &data, sizeof(data));
|
||||
}
|
||||
|
||||
#endif // USE_ESP8266_CRASH_HANDLER
|
||||
|
||||
@@ -6,6 +6,7 @@ extern "C" {
|
||||
}
|
||||
|
||||
#include "preferences.h"
|
||||
#include "rtc_layout.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/preferences_rtc.h"
|
||||
@@ -17,31 +18,26 @@ namespace esphome::esp8266 {
|
||||
ESPHOME_LOG_TAG(TAG, "preferences");
|
||||
|
||||
static constexpr uint32_t ESP_RTC_USER_MEM_START = 0x60001200;
|
||||
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128;
|
||||
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = RTC_USER_MEM_WORDS;
|
||||
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4;
|
||||
|
||||
// RTC memory layout:
|
||||
// RTC memory layout (sizes come from const.py through codegen):
|
||||
// - Eboot region: RTC words 0-31 (reserved, mapped from preference offset 78-109)
|
||||
// - Normal region: RTC words 32-109 (mapped from preference offset 0-77)
|
||||
// - Crash handler: RTC words 110-127 (reserved for crash_handler.cpp backtrace data)
|
||||
static constexpr uint32_t RTC_EBOOT_REGION_WORDS = 32; // Words 0-31 reserved for eboot
|
||||
static constexpr uint32_t RTC_NORMAL_REGION_WORDS = 78; // Words 32-109 for normal prefs
|
||||
static constexpr uint32_t RTC_EBOOT_REGION_WORDS = ESP8266_PREFERENCES_RTC_EBOOT_WORDS;
|
||||
static constexpr uint32_t RTC_NORMAL_REGION_WORDS = ESP8266_PREFERENCES_RTC_NORMAL_WORDS;
|
||||
static constexpr uint32_t PREF_TOTAL_WORDS = RTC_EBOOT_REGION_WORDS + RTC_NORMAL_REGION_WORDS; // 110
|
||||
static_assert(PREF_TOTAL_WORDS <= RTC_CRASH_BASE - RTC_SYSTEM_USER_BASE,
|
||||
"preference regions overlap the crash handler's RTC words");
|
||||
|
||||
// Maximum preference size in words (limited by uint8_t length_words field)
|
||||
static constexpr uint32_t MAX_PREFERENCE_WORDS = 255;
|
||||
|
||||
#define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START)
|
||||
|
||||
// Flash storage size depends on esp8266 -> restore_from_flash YAML option (default: false).
|
||||
// When enabled (USE_ESP8266_PREFERENCES_FLASH), all preferences default to flash and need
|
||||
// 128 words (512 bytes). When disabled, only explicit flash prefs use this storage so
|
||||
// 64 words (256 bytes) suffices since most preferences go to RTC memory instead.
|
||||
#ifdef USE_ESP8266_PREFERENCES_FLASH
|
||||
static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 128;
|
||||
#else
|
||||
static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 64;
|
||||
#endif
|
||||
// Smaller without restore_from_flash, when only explicit flash preferences live here
|
||||
static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = ESP8266_PREFERENCES_FLASH_WORDS;
|
||||
|
||||
static uint32_t
|
||||
s_flash_storage[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
@@ -60,7 +56,7 @@ static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) {
|
||||
if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
|
||||
return false;
|
||||
}
|
||||
if (index < 32 && s_prevent_write) {
|
||||
if (index < RTC_EBOOT_REGION_WORDS && s_prevent_write) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -123,7 +119,7 @@ static bool load_from_rtc(size_t offset, uint32_t *data, size_t len) {
|
||||
|
||||
// Maximum buffer for any single preference - bounded by storage sizes.
|
||||
// Flash prefs: bounded by ESP8266_FLASH_STORAGE_SIZE (128 or 64 words).
|
||||
// RTC prefs: bounded by RTC_NORMAL_REGION_WORDS (96) - a single pref can't span both RTC regions.
|
||||
// RTC prefs: bounded by RTC_NORMAL_REGION_WORDS (78) - a single pref can't span both RTC regions.
|
||||
static constexpr size_t PREF_MAX_BUFFER_WORDS =
|
||||
ESP8266_FLASH_STORAGE_SIZE > RTC_NORMAL_REGION_WORDS ? ESP8266_FLASH_STORAGE_SIZE : RTC_NORMAL_REGION_WORDS;
|
||||
|
||||
@@ -180,8 +176,8 @@ ESPPreferenceObject ESP8266Preferences::make_preference(size_t length, uint32_t
|
||||
} else {
|
||||
uint32_t start = this->current_offset;
|
||||
bool in_normal = start < RTC_NORMAL_REGION_WORDS;
|
||||
// Normal: offset 0-95 maps to RTC offset 32-127
|
||||
// Eboot: offset 96-127 maps to RTC offset 0-31
|
||||
// Normal: offset 0-77 maps to RTC offset 32-109
|
||||
// Eboot: offset 78-109 maps to RTC offset 0-31
|
||||
if (in_normal && start + total_words > RTC_NORMAL_REGION_WORDS) {
|
||||
// start is in normal but end is not -> switch to Eboot
|
||||
this->current_offset = start = RTC_NORMAL_REGION_WORDS;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace esphome::esp8266 {
|
||||
|
||||
// User RTC memory is system blocks 64-191, one 4-byte word each. preferences.cpp owns the first
|
||||
// words, crash_handler.cpp the last; const.py hands preferences.cpp its region sizes.
|
||||
static constexpr uint32_t RTC_SYSTEM_USER_BASE = 64;
|
||||
static constexpr uint32_t RTC_USER_MEM_WORDS = 128;
|
||||
static constexpr uint32_t RTC_CRASH_WORDS = 18;
|
||||
static constexpr uint32_t RTC_CRASH_BASE = RTC_SYSTEM_USER_BASE + RTC_USER_MEM_WORDS - RTC_CRASH_WORDS;
|
||||
|
||||
} // namespace esphome::esp8266
|
||||
|
||||
#endif // USE_ESP8266
|
||||
@@ -597,6 +597,9 @@
|
||||
#define USE_ESP8266_LOGGER_SERIAL
|
||||
#define USE_ESP8266_LOGGER_SERIAL1
|
||||
#define USE_ESP8266_PREFERENCES_FLASH
|
||||
#define ESP8266_PREFERENCES_FLASH_WORDS 128
|
||||
#define ESP8266_PREFERENCES_RTC_EBOOT_WORDS 32
|
||||
#define ESP8266_PREFERENCES_RTC_NORMAL_WORDS 78
|
||||
#define USE_ESP8266_UART_SERIAL
|
||||
#define USE_ESP8266_UART_SERIAL1
|
||||
#define USE_HTTP_REQUEST_ESP8266_HTTPS
|
||||
|
||||
@@ -102,3 +102,17 @@ def is_in_flash(value: str) -> bool:
|
||||
if not in_flash:
|
||||
request_rtc_storage()
|
||||
return in_flash
|
||||
|
||||
|
||||
def reserve_preferences(
|
||||
count: int, value_bytes: int, in_flash: bool | None = None
|
||||
) -> None:
|
||||
"""Reserve saved values from to_code so a platform with a small store can check they all fit.
|
||||
|
||||
in_flash is the value's own storage (is_in_flash(config[CONF_STORAGE])); leave it None for a
|
||||
component that calls the two-argument make_preference() and takes the platform default.
|
||||
"""
|
||||
if CORE.is_esp8266:
|
||||
from esphome.components.esp8266.const import reserve_preferences as reserve
|
||||
|
||||
reserve(count, value_bytes, in_flash)
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
"""The config-time fit check must agree with make_preference() in preferences.cpp."""
|
||||
|
||||
import asyncio
|
||||
|
||||
import pytest
|
||||
|
||||
from esphome import preferences
|
||||
from esphome.components.esp8266.const import (
|
||||
KEY_ESP8266,
|
||||
KEY_PREFERENCES,
|
||||
check_preferences_fit,
|
||||
preference_words,
|
||||
reserve_preferences,
|
||||
)
|
||||
from esphome.const import KEY_CORE, KEY_TARGET_PLATFORM
|
||||
from esphome.core import CORE, EsphomeError
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def esp8266_core() -> None:
|
||||
CORE.data[KEY_CORE] = {KEY_TARGET_PLATFORM: "esp8266"}
|
||||
CORE.data[KEY_ESP8266] = {}
|
||||
CORE.config = {KEY_ESP8266: {"restore_from_flash": False}}
|
||||
|
||||
|
||||
def _check() -> None:
|
||||
asyncio.run(check_preferences_fit())
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("value_bytes", "words"), [(0, 1), (1, 2), (4, 2), (5, 3), (94, 25)]
|
||||
)
|
||||
def test_preference_words_rounds_up_and_adds_the_checksum(
|
||||
value_bytes: int, words: int
|
||||
) -> None:
|
||||
assert preference_words(value_bytes) == words
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("restore_from_flash", "in_flash", "fitting", "match"),
|
||||
[
|
||||
# 25 words each: 3 in the 78-word region and 1 in the 32-word one
|
||||
(False, False, 4, "RTC memory has 110"),
|
||||
(False, None, 4, "RTC memory has 110"),
|
||||
# an explicit RTC value stays in RTC whatever restore_from_flash says
|
||||
(True, False, 4, "RTC memory has 110"),
|
||||
# 125 of 128 flash words, by the two-argument default or explicitly
|
||||
(True, None, 5, "flash store has 128"),
|
||||
(True, True, 5, "flash store has 128"),
|
||||
# explicit flash values without restore_from_flash use the 64-word store
|
||||
(False, True, 2, "flash store has 64"),
|
||||
],
|
||||
)
|
||||
def test_reservations_add_up_against_the_store_they_land_in(
|
||||
restore_from_flash: bool, in_flash: bool | None, fitting: int, match: str
|
||||
) -> None:
|
||||
CORE.config[KEY_ESP8266]["restore_from_flash"] = restore_from_flash
|
||||
reserve_preferences(fitting - 1, 94, in_flash) # one component
|
||||
reserve_preferences(1, 94, in_flash) # another component
|
||||
_check()
|
||||
reserve_preferences(1, 94, in_flash)
|
||||
with pytest.raises(EsphomeError, match=match):
|
||||
_check()
|
||||
|
||||
|
||||
def test_values_that_cannot_split_between_the_regions_fail() -> None:
|
||||
# 5 x 20 words is 100 of 110, but the normal region takes 3 and the eboot region 1
|
||||
reserve_preferences(5, 76)
|
||||
with pytest.raises(EsphomeError, match="cannot span"):
|
||||
_check()
|
||||
|
||||
|
||||
def test_a_value_larger_than_either_region_fails() -> None:
|
||||
reserve_preferences(1, 400)
|
||||
with pytest.raises(EsphomeError, match="cannot span"):
|
||||
_check()
|
||||
|
||||
|
||||
def test_a_split_that_works_in_some_order_passes_with_a_warning(
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
# 78 + 32: one value fills each region exactly
|
||||
reserve_preferences(1, 308)
|
||||
reserve_preferences(1, 124)
|
||||
_check()
|
||||
assert "depends on setup order" in caplog.text
|
||||
|
||||
|
||||
def test_nothing_reserved_passes_quietly(caplog: pytest.LogCaptureFixture) -> None:
|
||||
_check()
|
||||
assert caplog.text == ""
|
||||
|
||||
|
||||
def test_the_check_runs_as_a_scheduled_job() -> None:
|
||||
reserve_preferences(3, 400)
|
||||
CORE.add_job(check_preferences_fit)
|
||||
with pytest.raises(EsphomeError, match="cannot span"):
|
||||
CORE.flush_tasks()
|
||||
|
||||
|
||||
def test_components_reserve_through_esphome_preferences() -> None:
|
||||
preferences.reserve_preferences(2, 94, in_flash=True)
|
||||
assert CORE.data[KEY_ESP8266][KEY_PREFERENCES] == [(25, True), (25, True)]
|
||||
CORE.data[KEY_CORE] = {KEY_TARGET_PLATFORM: "esp32"}
|
||||
CORE.data[KEY_ESP8266] = {}
|
||||
preferences.reserve_preferences(2, 94)
|
||||
assert KEY_PREFERENCES not in CORE.data[KEY_ESP8266]
|
||||
Reference in New Issue
Block a user