Add temperature and humidity entities to area registry (#135423)

* Add temperature and humidity entities to area registry

* Fix service test

* Add validation

* ABC

* More ABC

* More ABC 2

* Fix tests

* ABC 3

* ABC 4
pull/131382/head
Paulus Schoutsen 2025-01-16 08:25:26 -05:00 committed by GitHub
parent 9f7a38f189
commit 5cf56207fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 258 additions and 13 deletions

View File

@ -41,10 +41,12 @@ def websocket_list_areas(
vol.Required("type"): "config/area_registry/create",
vol.Optional("aliases"): list,
vol.Optional("floor_id"): str,
vol.Optional("humidity_entity_id"): vol.Any(str, None),
vol.Optional("icon"): str,
vol.Optional("labels"): [str],
vol.Required("name"): str,
vol.Optional("picture"): vol.Any(str, None),
vol.Optional("temperature_entity_id"): vol.Any(str, None),
}
)
@websocket_api.require_admin
@ -107,10 +109,12 @@ def websocket_delete_area(
vol.Optional("aliases"): list,
vol.Required("area_id"): str,
vol.Optional("floor_id"): vol.Any(str, None),
vol.Optional("humidity_entity_id"): vol.Any(str, None),
vol.Optional("icon"): vol.Any(str, None),
vol.Optional("labels"): [str],
vol.Optional("name"): str,
vol.Optional("picture"): vol.Any(str, None),
vol.Optional("temperature_entity_id"): vol.Any(str, None),
}
)
@websocket_api.require_admin

View File

@ -9,6 +9,7 @@ from dataclasses import dataclass, field
from datetime import datetime
from typing import TYPE_CHECKING, Any, Literal, TypedDict
from homeassistant.const import ATTR_DEVICE_CLASS
from homeassistant.core import HomeAssistant, callback
from homeassistant.util.dt import utc_from_timestamp, utcnow
from homeassistant.util.event_type import EventType
@ -38,7 +39,7 @@ EVENT_AREA_REGISTRY_UPDATED: EventType[EventAreaRegistryUpdatedData] = EventType
)
STORAGE_KEY = "core.area_registry"
STORAGE_VERSION_MAJOR = 1
STORAGE_VERSION_MINOR = 7
STORAGE_VERSION_MINOR = 8
class _AreaStoreData(TypedDict):
@ -46,11 +47,13 @@ class _AreaStoreData(TypedDict):
aliases: list[str]
floor_id: str | None
humidity_entity_id: str | None
icon: str | None
id: str
labels: list[str]
name: str
picture: str | None
temperature_entity_id: str | None
created_at: str
modified_at: str
@ -74,10 +77,12 @@ class AreaEntry(NormalizedNameBaseRegistryEntry):
aliases: set[str]
floor_id: str | None
humidity_entity_id: str | None
icon: str | None
id: str
labels: set[str] = field(default_factory=set)
picture: str | None
temperature_entity_id: str | None
_cache: dict[str, Any] = field(default_factory=dict, compare=False, init=False)
@under_cached_property
@ -89,10 +94,12 @@ class AreaEntry(NormalizedNameBaseRegistryEntry):
"aliases": list(self.aliases),
"area_id": self.id,
"floor_id": self.floor_id,
"humidity_entity_id": self.humidity_entity_id,
"icon": self.icon,
"labels": list(self.labels),
"name": self.name,
"picture": self.picture,
"temperature_entity_id": self.temperature_entity_id,
"created_at": self.created_at.timestamp(),
"modified_at": self.modified_at.timestamp(),
}
@ -138,11 +145,17 @@ class AreaRegistryStore(Store[AreasRegistryStoreData]):
area["labels"] = []
if old_minor_version < 7:
# Version 1.7 adds created_at and modiefied_at
# Version 1.7 adds created_at and modified_at
created_at = utc_from_timestamp(0).isoformat()
for area in old_data["areas"]:
area["created_at"] = area["modified_at"] = created_at
if old_minor_version < 8:
# Version 1.8 adds humidity_entity_id and temperature_entity_id
for area in old_data["areas"]:
area["humidity_entity_id"] = None
area["temperature_entity_id"] = None
if old_major_version > 1:
raise NotImplementedError
return old_data # type: ignore[return-value]
@ -242,11 +255,14 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
*,
aliases: set[str] | None = None,
floor_id: str | None = None,
humidity_entity_id: str | None = None,
icon: str | None = None,
labels: set[str] | None = None,
picture: str | None = None,
temperature_entity_id: str | None = None,
) -> AreaEntry:
"""Create a new area."""
self.hass.verify_event_loop_thread("area_registry.async_create")
if area := self.async_get_area_by_name(name):
@ -254,14 +270,22 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
f"The name {name} ({area.normalized_name}) is already in use"
)
if humidity_entity_id is not None:
_validate_humidity_entity(self.hass, humidity_entity_id)
if temperature_entity_id is not None:
_validate_temperature_entity(self.hass, temperature_entity_id)
area = AreaEntry(
aliases=aliases or set(),
floor_id=floor_id,
humidity_entity_id=humidity_entity_id,
icon=icon,
id=self._generate_id(name),
labels=labels or set(),
name=name,
picture=picture,
temperature_entity_id=temperature_entity_id,
)
area_id = area.id
self.areas[area_id] = area
@ -298,20 +322,24 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
*,
aliases: set[str] | UndefinedType = UNDEFINED,
floor_id: str | None | UndefinedType = UNDEFINED,
humidity_entity_id: str | None | UndefinedType = UNDEFINED,
icon: str | None | UndefinedType = UNDEFINED,
labels: set[str] | UndefinedType = UNDEFINED,
name: str | UndefinedType = UNDEFINED,
picture: str | None | UndefinedType = UNDEFINED,
temperature_entity_id: str | None | UndefinedType = UNDEFINED,
) -> AreaEntry:
"""Update name of area."""
updated = self._async_update(
area_id,
aliases=aliases,
floor_id=floor_id,
humidity_entity_id=humidity_entity_id,
icon=icon,
labels=labels,
name=name,
picture=picture,
temperature_entity_id=temperature_entity_id,
)
# Since updated may be the old or the new and we always fire
# an event even if nothing has changed we cannot use async_fire_internal
@ -330,10 +358,12 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
*,
aliases: set[str] | UndefinedType = UNDEFINED,
floor_id: str | None | UndefinedType = UNDEFINED,
humidity_entity_id: str | None | UndefinedType = UNDEFINED,
icon: str | None | UndefinedType = UNDEFINED,
labels: set[str] | UndefinedType = UNDEFINED,
name: str | UndefinedType = UNDEFINED,
picture: str | None | UndefinedType = UNDEFINED,
temperature_entity_id: str | None | UndefinedType = UNDEFINED,
) -> AreaEntry:
"""Update name of area."""
old = self.areas[area_id]
@ -342,14 +372,22 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
attr_name: value
for attr_name, value in (
("aliases", aliases),
("floor_id", floor_id),
("humidity_entity_id", humidity_entity_id),
("icon", icon),
("labels", labels),
("picture", picture),
("floor_id", floor_id),
("temperature_entity_id", temperature_entity_id),
)
if value is not UNDEFINED and value != getattr(old, attr_name)
}
if "humidity_entity_id" in new_values and humidity_entity_id is not None:
_validate_humidity_entity(self.hass, new_values["humidity_entity_id"])
if "temperature_entity_id" in new_values and temperature_entity_id is not None:
_validate_temperature_entity(self.hass, new_values["temperature_entity_id"])
if name is not UNDEFINED and name != old.name:
new_values["name"] = name
@ -378,11 +416,13 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
areas[area["id"]] = AreaEntry(
aliases=set(area["aliases"]),
floor_id=area["floor_id"],
humidity_entity_id=area["humidity_entity_id"],
icon=area["icon"],
id=area["id"],
labels=set(area["labels"]),
name=area["name"],
picture=area["picture"],
temperature_entity_id=area["temperature_entity_id"],
created_at=datetime.fromisoformat(area["created_at"]),
modified_at=datetime.fromisoformat(area["modified_at"]),
)
@ -398,11 +438,13 @@ class AreaRegistry(BaseRegistry[AreasRegistryStoreData]):
{
"aliases": list(entry.aliases),
"floor_id": entry.floor_id,
"humidity_entity_id": entry.humidity_entity_id,
"icon": entry.icon,
"id": entry.id,
"labels": list(entry.labels),
"name": entry.name,
"picture": entry.picture,
"temperature_entity_id": entry.temperature_entity_id,
"created_at": entry.created_at.isoformat(),
"modified_at": entry.modified_at.isoformat(),
}
@ -477,3 +519,33 @@ def async_entries_for_floor(registry: AreaRegistry, floor_id: str) -> list[AreaE
def async_entries_for_label(registry: AreaRegistry, label_id: str) -> list[AreaEntry]:
"""Return entries that match a label."""
return registry.areas.get_areas_for_label(label_id)
def _validate_temperature_entity(hass: HomeAssistant, entity_id: str) -> None:
"""Validate temperature entity."""
# pylint: disable=import-outside-toplevel
from homeassistant.components.sensor import SensorDeviceClass
if not (state := hass.states.get(entity_id)):
raise ValueError(f"Entity {entity_id} does not exist")
if (
state.domain != "sensor"
or state.attributes.get(ATTR_DEVICE_CLASS) != SensorDeviceClass.TEMPERATURE
):
raise ValueError(f"Entity {entity_id} is not a temperature sensor")
def _validate_humidity_entity(hass: HomeAssistant, entity_id: str) -> None:
"""Validate humidity entity."""
# pylint: disable=import-outside-toplevel
from homeassistant.components.sensor import SensorDeviceClass
if not (state := hass.states.get(entity_id)):
raise ValueError(f"Entity {entity_id} does not exist")
if (
state.domain != "sensor"
or state.attributes.get(ATTR_DEVICE_CLASS) != SensorDeviceClass.HUMIDITY
):
raise ValueError(f"Entity {entity_id} is not a humidity sensor")

View File

@ -7,6 +7,13 @@ import pytest
from pytest_unordered import unordered
from homeassistant.components.config import area_registry
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_UNIT_OF_MEASUREMENT,
PERCENTAGE,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import area_registry as ar
from homeassistant.util.dt import utcnow
@ -24,10 +31,32 @@ async def client_fixture(
return await hass_ws_client(hass)
@pytest.fixture
async def mock_temperature_humidity_entity(hass: HomeAssistant) -> None:
"""Mock temperature and humidity sensors."""
hass.states.async_set(
"sensor.mock_temperature",
"20",
{
ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE,
ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS,
},
)
hass.states.async_set(
"sensor.mock_humidity",
"50",
{
ATTR_DEVICE_CLASS: SensorDeviceClass.HUMIDITY,
ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE,
},
)
async def test_list_areas(
client: MockHAClientWebSocket,
area_registry: ar.AreaRegistry,
freezer: FrozenDateTimeFactory,
mock_temperature_humidity_entity: None,
) -> None:
"""Test list entries."""
created_area1 = datetime.fromisoformat("2024-07-16T13:30:00.900075+00:00")
@ -39,10 +68,12 @@ async def test_list_areas(
area2 = area_registry.async_create(
"mock 2",
aliases={"alias_1", "alias_2"},
icon="mdi:garage",
picture="/image/example.png",
floor_id="first_floor",
humidity_entity_id="sensor.mock_humidity",
icon="mdi:garage",
labels={"label_1", "label_2"},
picture="/image/example.png",
temperature_entity_id="sensor.mock_temperature",
)
await client.send_json_auto_id({"type": "config/area_registry/list"})
@ -52,24 +83,28 @@ async def test_list_areas(
{
"aliases": [],
"area_id": area1.id,
"created_at": created_area1.timestamp(),
"floor_id": None,
"humidity_entity_id": None,
"icon": None,
"labels": [],
"modified_at": created_area1.timestamp(),
"name": "mock 1",
"picture": None,
"created_at": created_area1.timestamp(),
"modified_at": created_area1.timestamp(),
"temperature_entity_id": None,
},
{
"aliases": unordered(["alias_1", "alias_2"]),
"area_id": area2.id,
"created_at": created_area2.timestamp(),
"floor_id": "first_floor",
"humidity_entity_id": "sensor.mock_humidity",
"icon": "mdi:garage",
"labels": unordered(["label_1", "label_2"]),
"modified_at": created_area2.timestamp(),
"name": "mock 2",
"picture": "/image/example.png",
"created_at": created_area2.timestamp(),
"modified_at": created_area2.timestamp(),
"temperature_entity_id": "sensor.mock_temperature",
},
]
@ -78,6 +113,7 @@ async def test_create_area(
client: MockHAClientWebSocket,
area_registry: ar.AreaRegistry,
freezer: FrozenDateTimeFactory,
mock_temperature_humidity_entity: None,
) -> None:
"""Test create entry."""
# Create area with only mandatory parameters
@ -97,6 +133,8 @@ async def test_create_area(
"picture": None,
"created_at": utcnow().timestamp(),
"modified_at": utcnow().timestamp(),
"temperature_entity_id": None,
"humidity_entity_id": None,
}
assert len(area_registry.areas) == 1
@ -109,12 +147,15 @@ async def test_create_area(
"labels": ["label_1", "label_2"],
"name": "mock 2",
"picture": "/image/example.png",
"temperature_entity_id": "sensor.mock_temperature",
"humidity_entity_id": "sensor.mock_humidity",
"type": "config/area_registry/create",
}
)
msg = await client.receive_json()
assert msg["success"]
assert msg["result"] == {
"aliases": unordered(["alias_1", "alias_2"]),
"area_id": ANY,
@ -125,6 +166,8 @@ async def test_create_area(
"picture": "/image/example.png",
"created_at": utcnow().timestamp(),
"modified_at": utcnow().timestamp(),
"temperature_entity_id": "sensor.mock_temperature",
"humidity_entity_id": "sensor.mock_humidity",
}
assert len(area_registry.areas) == 2
@ -185,6 +228,7 @@ async def test_update_area(
client: MockHAClientWebSocket,
area_registry: ar.AreaRegistry,
freezer: FrozenDateTimeFactory,
mock_temperature_humidity_entity: None,
) -> None:
"""Test update entry."""
created_at = datetime.fromisoformat("2024-07-16T13:30:00.900075+00:00")
@ -195,14 +239,16 @@ async def test_update_area(
await client.send_json_auto_id(
{
"type": "config/area_registry/update",
"aliases": ["alias_1", "alias_2"],
"area_id": area.id,
"floor_id": "first_floor",
"humidity_entity_id": "sensor.mock_humidity",
"icon": "mdi:garage",
"labels": ["label_1", "label_2"],
"name": "mock 2",
"picture": "/image/example.png",
"type": "config/area_registry/update",
"temperature_entity_id": "sensor.mock_temperature",
}
)
@ -212,10 +258,12 @@ async def test_update_area(
"aliases": unordered(["alias_1", "alias_2"]),
"area_id": area.id,
"floor_id": "first_floor",
"humidity_entity_id": "sensor.mock_humidity",
"icon": "mdi:garage",
"labels": unordered(["label_1", "label_2"]),
"name": "mock 2",
"picture": "/image/example.png",
"temperature_entity_id": "sensor.mock_temperature",
"created_at": created_at.timestamp(),
"modified_at": modified_at.timestamp(),
}
@ -226,13 +274,15 @@ async def test_update_area(
await client.send_json_auto_id(
{
"type": "config/area_registry/update",
"aliases": ["alias_1", "alias_1"],
"area_id": area.id,
"floor_id": None,
"humidity_entity_id": None,
"icon": None,
"labels": [],
"picture": None,
"type": "config/area_registry/update",
"temperature_entity_id": None,
}
)
@ -246,6 +296,8 @@ async def test_update_area(
"labels": [],
"name": "mock 2",
"picture": None,
"temperature_entity_id": None,
"humidity_entity_id": None,
"created_at": created_at.timestamp(),
"modified_at": modified_at.timestamp(),
}

View File

@ -7,6 +7,13 @@ from typing import Any
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_UNIT_OF_MEASUREMENT,
PERCENTAGE,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import (
area_registry as ar,
@ -18,6 +25,27 @@ from homeassistant.util.dt import utcnow
from tests.common import ANY, async_capture_events, flush_store
@pytest.fixture
async def mock_temperature_humidity_entity(hass: HomeAssistant) -> None:
"""Mock temperature and humidity sensors."""
hass.states.async_set(
"sensor.mock_temperature",
"20",
{
ATTR_DEVICE_CLASS: SensorDeviceClass.TEMPERATURE,
ATTR_UNIT_OF_MEASUREMENT: UnitOfTemperature.CELSIUS,
},
)
hass.states.async_set(
"sensor.mock_humidity",
"50",
{
ATTR_DEVICE_CLASS: SensorDeviceClass.HUMIDITY,
ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE,
},
)
async def test_list_areas(area_registry: ar.AreaRegistry) -> None:
"""Make sure that we can read areas."""
area_registry.async_create("mock")
@ -31,6 +59,7 @@ async def test_create_area(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
area_registry: ar.AreaRegistry,
mock_temperature_humidity_entity: None,
) -> None:
"""Make sure that we can create an area."""
update_events = async_capture_events(hass, ar.EVENT_AREA_REGISTRY_UPDATED)
@ -48,6 +77,8 @@ async def test_create_area(
picture=None,
created_at=utcnow(),
modified_at=utcnow(),
temperature_entity_id=None,
humidity_entity_id=None,
)
assert len(area_registry.areas) == 1
@ -67,6 +98,8 @@ async def test_create_area(
aliases={"alias_1", "alias_2"},
labels={"label1", "label2"},
picture="/image/example.png",
temperature_entity_id="sensor.mock_temperature",
humidity_entity_id="sensor.mock_humidity",
)
assert area2 == ar.AreaEntry(
@ -79,6 +112,8 @@ async def test_create_area(
picture="/image/example.png",
created_at=utcnow(),
modified_at=utcnow(),
temperature_entity_id="sensor.mock_temperature",
humidity_entity_id="sensor.mock_humidity",
)
assert len(area_registry.areas) == 2
assert area.created_at != area2.created_at
@ -164,6 +199,7 @@ async def test_update_area(
floor_registry: fr.FloorRegistry,
label_registry: lr.LabelRegistry,
freezer: FrozenDateTimeFactory,
mock_temperature_humidity_entity: None,
) -> None:
"""Make sure that we can read areas."""
created_at = datetime.fromisoformat("2024-01-01T01:00:00+00:00")
@ -184,6 +220,8 @@ async def test_update_area(
labels={"label1", "label2"},
name="mock1",
picture="/image/example.png",
temperature_entity_id="sensor.mock_temperature",
humidity_entity_id="sensor.mock_humidity",
)
assert updated_area != area
@ -197,6 +235,8 @@ async def test_update_area(
picture="/image/example.png",
created_at=created_at,
modified_at=modified_at,
temperature_entity_id="sensor.mock_temperature",
humidity_entity_id="sensor.mock_humidity",
)
assert len(area_registry.areas) == 1
@ -274,6 +314,55 @@ async def test_update_area_with_normalized_name_already_in_use(
assert len(area_registry.areas) == 2
@pytest.mark.parametrize(
("create_kwargs", "error_message"),
[
(
{"temperature_entity_id": "sensor.invalid"},
"Entity sensor.invalid does not exist",
),
(
{"temperature_entity_id": "light.kitchen"},
"Entity light.kitchen is not a temperature sensor",
),
(
{"temperature_entity_id": "sensor.random"},
"Entity sensor.random is not a temperature sensor",
),
(
{"humidity_entity_id": "sensor.invalid"},
"Entity sensor.invalid does not exist",
),
(
{"humidity_entity_id": "light.kitchen"},
"Entity light.kitchen is not a humidity sensor",
),
(
{"humidity_entity_id": "sensor.random"},
"Entity sensor.random is not a humidity sensor",
),
],
)
async def test_update_area_entity_validation(
hass: HomeAssistant,
area_registry: ar.AreaRegistry,
mock_temperature_humidity_entity: None,
create_kwargs: dict[str, Any],
error_message: str,
) -> None:
"""Make sure that we can't update an area with an invalid entity."""
area = area_registry.async_create("mock")
hass.states.async_set("light.kitchen", "on", {})
hass.states.async_set("sensor.random", "3", {})
with pytest.raises(ValueError) as e_info:
area_registry.async_update(area.id, **create_kwargs)
assert str(e_info.value) == error_message
assert area.temperature_entity_id is None
assert area.humidity_entity_id is None
async def test_load_area(hass: HomeAssistant, area_registry: ar.AreaRegistry) -> None:
"""Make sure that we can load/save data correctly."""
area1 = area_registry.async_create("mock1")
@ -298,6 +387,8 @@ async def test_loading_area_from_storage(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
"""Test loading stored areas on start."""
created_at = datetime.fromisoformat("2024-01-01T01:00:00+00:00")
modified_at = datetime.fromisoformat("2024-02-01T01:00:00+00:00")
hass_storage[ar.STORAGE_KEY] = {
"version": ar.STORAGE_VERSION_MAJOR,
"minor_version": ar.STORAGE_VERSION_MINOR,
@ -311,8 +402,10 @@ async def test_loading_area_from_storage(
"labels": ["mock-label1", "mock-label2"],
"name": "mock",
"picture": "blah",
"created_at": utcnow().isoformat(),
"modified_at": utcnow().isoformat(),
"created_at": created_at.isoformat(),
"modified_at": modified_at.isoformat(),
"temperature_entity_id": "sensor.mock_temperature",
"humidity_entity_id": "sensor.mock_humidity",
}
]
},
@ -322,6 +415,20 @@ async def test_loading_area_from_storage(
registry = ar.async_get(hass)
assert len(registry.areas) == 1
area = registry.areas["12345A"]
assert area == ar.AreaEntry(
aliases={"alias_1", "alias_2"},
floor_id="first_floor",
icon="mdi:garage",
id="12345A",
labels={"mock-label1", "mock-label2"},
name="mock",
picture="blah",
created_at=created_at,
modified_at=modified_at,
temperature_entity_id="sensor.mock_temperature",
humidity_entity_id="sensor.mock_humidity",
)
@pytest.mark.parametrize("load_registries", [False])
@ -359,6 +466,8 @@ async def test_migration_from_1_1(
"picture": None,
"created_at": "1970-01-01T00:00:00+00:00",
"modified_at": "1970-01-01T00:00:00+00:00",
"temperature_entity_id": None,
"humidity_entity_id": None,
}
]
},

View File

@ -122,6 +122,8 @@ def floor_area_mock(hass: HomeAssistant) -> None:
floor_id="test-floor",
icon=None,
picture=None,
temperature_entity_id=None,
humidity_entity_id=None,
)
area_in_floor_a = ar.AreaEntry(
id="area-a",
@ -130,6 +132,8 @@ def floor_area_mock(hass: HomeAssistant) -> None:
floor_id="floor-a",
icon=None,
picture=None,
temperature_entity_id=None,
humidity_entity_id=None,
)
mock_area_registry(
hass,
@ -284,6 +288,8 @@ def label_mock(hass: HomeAssistant) -> None:
icon=None,
labels={"label_area"},
picture=None,
temperature_entity_id=None,
humidity_entity_id=None,
)
area_without_labels = ar.AreaEntry(
id="area-no-labels",
@ -293,6 +299,8 @@ def label_mock(hass: HomeAssistant) -> None:
icon=None,
labels=set(),
picture=None,
temperature_entity_id=None,
humidity_entity_id=None,
)
mock_area_registry(
hass,