Improve iteration in device registry + add tests

This commit is contained in:
Erik
2026-08-24 08:40:06 +02:00
parent ad3c2445c6
commit 74776af4c1
3 changed files with 36 additions and 3 deletions
@@ -1,5 +1,6 @@
"""HTTP views to interact with the device registry.""" """HTTP views to interact with the device registry."""
from itertools import chain
import logging import logging
from typing import Any from typing import Any
@@ -92,7 +93,7 @@ def websocket_list_devices(
inner = b",".join( inner = b",".join(
[ [
entry.json_repr entry.json_repr
for entry in (*registry.devices, *registry.child_devices) for entry in chain(registry.devices, registry.child_devices)
if entry.json_repr is not None if entry.json_repr is not None
] ]
) )
@@ -1,6 +1,7 @@
"""Device functions for Home Assistant templates.""" """Device functions for Home Assistant templates."""
from collections.abc import Iterable from collections.abc import Iterable
from itertools import chain
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
import voluptuous as vol import voluptuous as vol
@@ -85,7 +86,7 @@ class DeviceExtension(BaseTemplateExtension):
return next( return next(
( (
device.id device.id
for device in (*dev_reg.devices, *dev_reg.child_devices) for device in chain(dev_reg.devices, dev_reg.child_devices)
if (name := device.name_by_user or device.name) if (name := device.name_by_user or device.name)
and (str(entity_id_or_device_name) == name) and (str(entity_id_or_device_name) == name)
), ),
+32 -1
View File
@@ -4365,6 +4365,34 @@ async def test_devices_collection_operations(
assert entry in device_registry.devices assert entry in device_registry.devices
async def test_child_devices_collection_operations(
device_registry: dr.DeviceRegistry,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the supported `Collection[ChildDeviceEntry]` surface of `child_devices`.
Iteration yields the entries (not the ids), `len()` returns the count, and
`ChildDeviceEntry` membership works. Unlike `DeviceRegistry.devices`, the child
collection is a plain read-only view, so mapping-style access, `.values()`, and
mutation are unavailable.
"""
_, child_device = _create_parent_and_child(
device_registry, mock_config_entry.entry_id
)
assert list(device_registry.child_devices) == [child_device]
assert [device.id for device in device_registry.child_devices] == [child_device.id]
assert len(device_registry.child_devices) == 1
assert child_device in device_registry.child_devices
with pytest.raises(TypeError):
_ = device_registry.child_devices[child_device.id] # type: ignore[index]
with pytest.raises(AttributeError):
device_registry.child_devices.values() # type: ignore[attr-defined]
with pytest.raises(TypeError):
device_registry.child_devices[child_device.id] = child_device # type: ignore[index]
@pytest.mark.parametrize( @pytest.mark.parametrize(
("integration_frame_path", "expectation", "expected_log"), ("integration_frame_path", "expectation", "expected_log"),
[ [
@@ -11123,7 +11151,10 @@ async def test_link_device_info_matching_child_raises(
# The child device is left untouched: not converted, no new device created # The child device is left untouched: not converted, no new device created
assert len(device_registry.devices) == 1 assert len(device_registry.devices) == 1
assert len(device_registry.child_devices) == 1 assert len(device_registry.child_devices) == 1
assert device_registry._child_devices[child_device.id] == child_device assert (
device_registry.async_get(child_device.id, include_main_devices=False)
== child_device
)
assert child_device.identifiers == {("test", "strip_outlet_1")} assert child_device.identifiers == {("test", "strip_outlet_1")}