[core] Persist esphome.area in StorageJSON (#16710)

This commit is contained in:
J. Nick Koston
2026-05-30 00:09:07 -05:00
committed by GitHub
parent 07a57d7557
commit f0202155b3
4 changed files with 73 additions and 0 deletions

View File

@@ -722,6 +722,7 @@ async def to_code(config: ConfigType) -> None:
# Process areas
all_areas: list[dict[str, str | core.ID]] = []
if CONF_AREA in config:
CORE.area = config[CONF_AREA][CONF_NAME]
all_areas.append(config[CONF_AREA])
all_areas.extend(config[CONF_AREAS])

View File

@@ -100,6 +100,7 @@ class StorageJSON:
framework: str | None = None,
core_platform: str | None = None,
toolchain: str | None = None,
area: str | None = None,
) -> None:
# Version of the storage JSON schema
assert storage_version is None or isinstance(storage_version, int)
@@ -138,6 +139,8 @@ class StorageJSON:
self.core_platform = core_platform
# The toolchain used for the build ("platformio" / "esp-idf")
self.toolchain = toolchain
# The area of the node
self.area = area
def as_dict(self):
return {
@@ -158,6 +161,7 @@ class StorageJSON:
"framework": self.framework,
"core_platform": self.core_platform,
"toolchain": self.toolchain,
"area": self.area,
}
def to_json(self):
@@ -195,6 +199,7 @@ class StorageJSON:
framework=esph.target_framework,
core_platform=esph.target_platform,
toolchain=esph.toolchain.value if esph.toolchain is not None else None,
area=esph.area,
)
@staticmethod
@@ -243,6 +248,7 @@ class StorageJSON:
framework = storage.get("framework")
core_platform = storage.get("core_platform")
toolchain = storage.get("toolchain")
area = storage.get("area")
return StorageJSON(
storage_version,
name,
@@ -261,6 +267,7 @@ class StorageJSON:
framework,
core_platform,
toolchain,
area,
)
@staticmethod

View File

@@ -140,6 +140,33 @@ def test_multiple_areas_and_devices(yaml_file: Callable[[str], str]) -> None:
}
@pytest.mark.asyncio
@pytest.mark.filterwarnings("ignore::RuntimeWarning")
@pytest.mark.parametrize(
("fixture", "expected_area"),
[
("legacy_string_area.yaml", "Living Room"),
("multiple_areas_devices.yaml", "Main Area"),
],
)
async def test_to_code_records_core_area(
yaml_file: Callable[[str], Path],
fixture: str,
expected_area: str,
) -> None:
"""``to_code`` records the node's area name on CORE for StorageJSON."""
result = load_config_from_fixture(yaml_file, fixture, FIXTURES_DIR)
assert result is not None
assert CORE.area is None
with patch("esphome.core.config.cg") as mock_cg:
mock_cg.RawStatement.side_effect = lambda *args, **kwargs: MagicMock()
mock_cg.RawExpression.side_effect = lambda *args, **kwargs: MagicMock()
await config.to_code(result[CONF_ESPHOME])
assert CORE.area == expected_area
def test_legacy_string_area(
yaml_file: Callable[[str], str], caplog: pytest.LogCaptureFixture
) -> None:

View File

@@ -205,6 +205,7 @@ def test_storage_json_as_dict() -> None:
no_mdns=True,
framework="arduino",
core_platform="esp32",
area="Living Room",
)
result = storage.as_dict()
@@ -233,6 +234,7 @@ def test_storage_json_as_dict() -> None:
assert result["no_mdns"] is True
assert result["framework"] == "arduino"
assert result["core_platform"] == "esp32"
assert result["area"] == "Living Room"
def test_storage_json_to_json() -> None:
@@ -309,6 +311,7 @@ def test_storage_json_from_esphome_core(setup_core: Path) -> None:
mock_core.config = {CONF_MDNS: {CONF_DISABLED: True}}
mock_core.target_framework = "esp-idf"
mock_core.toolchain = Toolchain.ESP_IDF
mock_core.area = "Living Room"
with patch("esphome.components.esp32.get_esp32_variant") as mock_variant:
mock_variant.return_value = "ESP32-C3"
@@ -329,6 +332,7 @@ def test_storage_json_from_esphome_core(setup_core: Path) -> None:
assert result.framework == "esp-idf"
assert result.core_platform == "esp32"
assert result.toolchain == "esp-idf"
assert result.area == "Living Room"
def test_storage_json_from_esphome_core_mdns_enabled(setup_core: Path) -> None:
@@ -729,3 +733,37 @@ def test_storage_json_load_legacy_esphomeyaml_version(tmp_path: Path) -> None:
assert result is not None
assert result.esphome_version == "1.14.0" # Should map to esphome_version
def test_storage_json_load_area(tmp_path: Path) -> None:
"""``area`` round-trips through load; absence loads as None."""
file_path = tmp_path / "with_area.json"
file_path.write_text(
json.dumps(
{
"storage_version": 1,
"name": "lamp",
"friendly_name": "Lamp",
"esp_platform": "ESP32",
"area": "Living Room",
}
)
)
result = storage_json.StorageJSON.load(file_path)
assert result is not None
assert result.area == "Living Room"
legacy_path = tmp_path / "no_area.json"
legacy_path.write_text(
json.dumps(
{
"storage_version": 1,
"name": "lamp",
"friendly_name": "Lamp",
"esp_platform": "ESP32",
}
)
)
legacy = storage_json.StorageJSON.load(legacy_path)
assert legacy is not None
assert legacy.area is None