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 4pull/131382/head
parent
9f7a38f189
commit
5cf56207fe
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue