mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
Allow a device to be connected to no or a single subentry of a config entry
This commit is contained in:
parent
6b4c27e700
commit
53fd84a5a4
@ -272,7 +272,7 @@ class DeviceEntry:
|
||||
|
||||
area_id: str | None = attr.ib(default=None)
|
||||
config_entries: set[str] = attr.ib(converter=set, factory=set)
|
||||
config_subentries: dict[str, set[str | None]] = attr.ib(factory=dict)
|
||||
config_subentries: dict[str, str | None] = attr.ib(factory=dict)
|
||||
configuration_url: str | None = attr.ib(default=None)
|
||||
connections: set[tuple[str, str]] = attr.ib(converter=set, factory=set)
|
||||
created_at: datetime = attr.ib(factory=utcnow)
|
||||
@ -312,10 +312,7 @@ class DeviceEntry:
|
||||
"area_id": self.area_id,
|
||||
"configuration_url": self.configuration_url,
|
||||
"config_entries": list(self.config_entries),
|
||||
"config_subentries": {
|
||||
config_entry_id: list(subentries)
|
||||
for config_entry_id, subentries in self.config_subentries.items()
|
||||
},
|
||||
"config_subentries": self.config_subentries,
|
||||
"connections": list(self.connections),
|
||||
"created_at": self.created_at.timestamp(),
|
||||
"disabled_by": self.disabled_by,
|
||||
@ -362,10 +359,7 @@ class DeviceEntry:
|
||||
# The config_entries list can be removed from the storage
|
||||
# representation in HA Core 2026.1
|
||||
"config_entries": list(self.config_entries),
|
||||
"config_subentries": {
|
||||
config_entry_id: list(subentries)
|
||||
for config_entry_id, subentries in self.config_subentries.items()
|
||||
},
|
||||
"config_subentries": self.config_subentries,
|
||||
"configuration_url": self.configuration_url,
|
||||
"connections": list(self.connections),
|
||||
"created_at": self.created_at,
|
||||
@ -395,7 +389,7 @@ class DeletedDeviceEntry:
|
||||
"""Deleted Device Registry Entry."""
|
||||
|
||||
config_entries: set[str] = attr.ib()
|
||||
config_subentries: dict[str, set[str | None]] = attr.ib()
|
||||
config_subentries: dict[str, str | None] = attr.ib()
|
||||
connections: set[tuple[str, str]] = attr.ib()
|
||||
identifiers: set[tuple[str, str]] = attr.ib()
|
||||
id: str = attr.ib()
|
||||
@ -415,7 +409,7 @@ class DeletedDeviceEntry:
|
||||
return DeviceEntry(
|
||||
# type ignores: likely https://github.com/python/mypy/issues/8625
|
||||
config_entries={config_entry_id}, # type: ignore[arg-type]
|
||||
config_subentries={config_entry_id: {config_subentry_id}},
|
||||
config_subentries={config_entry_id: config_subentry_id},
|
||||
connections=self.connections & connections, # type: ignore[arg-type]
|
||||
created_at=self.created_at,
|
||||
identifiers=self.identifiers & identifiers, # type: ignore[arg-type]
|
||||
@ -432,10 +426,7 @@ class DeletedDeviceEntry:
|
||||
# The config_entries list can be removed from the storage
|
||||
# representation in HA Core 2026.1
|
||||
"config_entries": list(self.config_entries),
|
||||
"config_subentries": {
|
||||
config_entry_id: list(subentries)
|
||||
for config_entry_id, subentries in self.config_subentries.items()
|
||||
},
|
||||
"config_subentries": self.config_subentries,
|
||||
"connections": list(self.connections),
|
||||
"created_at": self.created_at,
|
||||
"identifiers": list(self.identifiers),
|
||||
@ -532,12 +523,12 @@ class DeviceRegistryStore(storage.Store[dict[str, list[dict[str, Any]]]]):
|
||||
# Introduced in 2025.1
|
||||
for device in old_data["devices"]:
|
||||
device["config_subentries"] = {
|
||||
config_entry_id: {None}
|
||||
config_entry_id: None
|
||||
for config_entry_id in device["config_entries"]
|
||||
}
|
||||
for device in old_data["deleted_devices"]:
|
||||
device["config_subentries"] = {
|
||||
config_entry_id: {None}
|
||||
config_entry_id: None
|
||||
for config_entry_id in device["config_entries"]
|
||||
}
|
||||
|
||||
@ -913,7 +904,6 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
||||
new_connections: set[tuple[str, str]] | UndefinedType = UNDEFINED,
|
||||
new_identifiers: set[tuple[str, str]] | UndefinedType = UNDEFINED,
|
||||
remove_config_entry_id: str | UndefinedType = UNDEFINED,
|
||||
remove_config_subentry_id: str | None | UndefinedType = UNDEFINED,
|
||||
serial_number: str | None | UndefinedType = UNDEFINED,
|
||||
suggested_area: str | None | UndefinedType = UNDEFINED,
|
||||
sw_version: str | None | UndefinedType = UNDEFINED,
|
||||
@ -922,7 +912,6 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
||||
"""Update device attributes.
|
||||
|
||||
:param add_config_subentry_id: Add the device to a specific subentry of add_config_entry_id
|
||||
:param remove_config_subentry_id: Remove the device from a specific subentry of remove_config_subentry_id
|
||||
"""
|
||||
old = self.devices[device_id]
|
||||
|
||||
@ -957,14 +946,6 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
||||
f"Config entry {add_config_entry_id} has no subentry {add_config_subentry_id}"
|
||||
)
|
||||
|
||||
if (
|
||||
remove_config_subentry_id is not UNDEFINED
|
||||
and remove_config_entry_id is UNDEFINED
|
||||
):
|
||||
raise HomeAssistantError(
|
||||
"Can't remove config subentry without specifying config entry"
|
||||
)
|
||||
|
||||
if not new_connections and not new_identifiers:
|
||||
raise HomeAssistantError(
|
||||
"A device must have at least one of identifiers or connections"
|
||||
@ -999,6 +980,15 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
||||
# Interpret not specifying a subentry as None (the main entry)
|
||||
add_config_subentry_id = None
|
||||
|
||||
if (
|
||||
add_config_entry_id in config_subentries
|
||||
and config_subentries[add_config_entry_id] != add_config_subentry_id
|
||||
):
|
||||
raise HomeAssistantError(
|
||||
f"Device is already linked to config entry {add_config_entry_id} "
|
||||
"with subentry {config_subentries[add_config_entry_id]}"
|
||||
)
|
||||
|
||||
primary_entry_id = old.primary_config_entry
|
||||
if (
|
||||
device_info_type == "primary"
|
||||
@ -1019,46 +1009,25 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
||||
if add_config_entry_id not in old.config_entries:
|
||||
config_entries = old.config_entries | {add_config_entry_id}
|
||||
config_subentries = old.config_subentries | {
|
||||
add_config_entry_id: {add_config_subentry_id}
|
||||
}
|
||||
elif (
|
||||
add_config_subentry_id not in old.config_subentries[add_config_entry_id]
|
||||
):
|
||||
config_subentries = old.config_subentries | {
|
||||
add_config_entry_id: old.config_subentries[add_config_entry_id]
|
||||
| {add_config_subentry_id}
|
||||
add_config_entry_id: add_config_subentry_id
|
||||
}
|
||||
|
||||
if (
|
||||
remove_config_entry_id is not UNDEFINED
|
||||
and remove_config_entry_id in config_entries
|
||||
):
|
||||
if remove_config_subentry_id is UNDEFINED:
|
||||
config_subentries = dict(old.config_subentries)
|
||||
del config_subentries[remove_config_entry_id]
|
||||
elif (
|
||||
remove_config_subentry_id
|
||||
in old.config_subentries[remove_config_entry_id]
|
||||
):
|
||||
config_subentries = old.config_subentries | {
|
||||
remove_config_entry_id: old.config_subentries[
|
||||
remove_config_entry_id
|
||||
]
|
||||
- {remove_config_subentry_id}
|
||||
}
|
||||
if not config_subentries[remove_config_entry_id]:
|
||||
del config_subentries[remove_config_entry_id]
|
||||
config_subentries = dict(config_subentries)
|
||||
del config_subentries[remove_config_entry_id]
|
||||
|
||||
if remove_config_entry_id not in config_subentries:
|
||||
if config_entries == {remove_config_entry_id}:
|
||||
self.async_remove_device(device_id)
|
||||
return None
|
||||
if config_entries == {remove_config_entry_id}:
|
||||
self.async_remove_device(device_id)
|
||||
return None
|
||||
|
||||
if remove_config_entry_id == old.primary_config_entry:
|
||||
new_values["primary_config_entry"] = None
|
||||
old_values["primary_config_entry"] = old.primary_config_entry
|
||||
if remove_config_entry_id == old.primary_config_entry:
|
||||
new_values["primary_config_entry"] = None
|
||||
old_values["primary_config_entry"] = old.primary_config_entry
|
||||
|
||||
config_entries = config_entries - {remove_config_entry_id}
|
||||
config_entries = config_entries - {remove_config_entry_id}
|
||||
|
||||
if config_entries != old.config_entries:
|
||||
new_values["config_entries"] = config_entries
|
||||
@ -1254,12 +1223,7 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
||||
devices[device["id"]] = DeviceEntry(
|
||||
area_id=device["area_id"],
|
||||
config_entries=set(device["config_subentries"]),
|
||||
config_subentries={
|
||||
config_entry_id: set(subentries)
|
||||
for config_entry_id, subentries in device[
|
||||
"config_subentries"
|
||||
].items()
|
||||
},
|
||||
config_subentries=device["config_subentries"],
|
||||
configuration_url=device["configuration_url"],
|
||||
# type ignores (if tuple arg was cast): likely https://github.com/python/mypy/issues/8625
|
||||
connections={
|
||||
@ -1299,12 +1263,7 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
||||
for device in data["deleted_devices"]:
|
||||
deleted_devices[device["id"]] = DeletedDeviceEntry(
|
||||
config_entries=set(device["config_entries"]),
|
||||
config_subentries={
|
||||
config_entry_id: set(subentries)
|
||||
for config_entry_id, subentries in device[
|
||||
"config_subentries"
|
||||
].items()
|
||||
},
|
||||
config_subentries=device["config_subentries"],
|
||||
connections={tuple(conn) for conn in device["connections"]},
|
||||
created_at=datetime.fromisoformat(device["created_at"]),
|
||||
identifiers={tuple(iden) for iden in device["identifiers"]},
|
||||
@ -1359,22 +1318,23 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
||||
now_time = time.time()
|
||||
now_time = time.time()
|
||||
for device in self.devices.get_devices_for_config_entry_id(config_entry_id):
|
||||
if device.config_subentries[config_entry_id] != config_subentry_id:
|
||||
continue
|
||||
self.async_update_device(
|
||||
device.id,
|
||||
remove_config_entry_id=config_entry_id,
|
||||
remove_config_subentry_id=config_subentry_id,
|
||||
)
|
||||
for deleted_device in list(self.deleted_devices.values()):
|
||||
config_entries = deleted_device.config_entries
|
||||
config_subentries = deleted_device.config_subentries
|
||||
if (
|
||||
config_entry_id not in config_subentries
|
||||
or config_subentry_id not in config_subentries[config_entry_id]
|
||||
or config_subentries[config_entry_id] != config_subentry_id
|
||||
):
|
||||
continue
|
||||
if config_subentries == {config_entry_id: {config_subentry_id}}:
|
||||
# We're removing the last config subentry from the last config
|
||||
# entry, add a time stamp when the deleted device became orphaned
|
||||
if config_subentries == {config_entry_id: config_subentry_id}:
|
||||
# We're removing the last config entry, add a time stamp
|
||||
# when the deleted device became orphaned
|
||||
self.deleted_devices[deleted_device.id] = attr.evolve(
|
||||
deleted_device,
|
||||
orphaned_timestamp=now_time,
|
||||
@ -1382,13 +1342,9 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
||||
config_subentries={},
|
||||
)
|
||||
else:
|
||||
config_subentries = config_subentries | {
|
||||
config_entry_id: config_subentries[config_entry_id]
|
||||
- {config_subentry_id}
|
||||
}
|
||||
if not config_subentries[config_entry_id]:
|
||||
del config_subentries[config_entry_id]
|
||||
config_entries = config_entries - {config_entry_id}
|
||||
config_subentries = dict(config_subentries)
|
||||
del config_subentries[config_entry_id]
|
||||
config_entries = config_entries - {config_entry_id}
|
||||
# No need to reindex here since we currently
|
||||
# do not have a lookup by config entry
|
||||
self.deleted_devices[deleted_device.id] = attr.evolve(
|
||||
|
@ -65,7 +65,7 @@ async def test_list_devices(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [entry.entry_id],
|
||||
"config_subentries": {entry.entry_id: [None]},
|
||||
"config_subentries": {entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [["ethernet", "12:34:56:78:90:AB:CD:EF"]],
|
||||
"created_at": utcnow().timestamp(),
|
||||
@ -88,7 +88,7 @@ async def test_list_devices(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [entry.entry_id],
|
||||
"config_subentries": {entry.entry_id: [None]},
|
||||
"config_subentries": {entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": utcnow().timestamp(),
|
||||
@ -123,7 +123,7 @@ async def test_list_devices(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [entry.entry_id],
|
||||
"config_subentries": {entry.entry_id: [None]},
|
||||
"config_subentries": {entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [["ethernet", "12:34:56:78:90:AB:CD:EF"]],
|
||||
"created_at": utcnow().timestamp(),
|
||||
|
@ -185,12 +185,6 @@ async def test_multiple_config_subentries(
|
||||
title="Mock title",
|
||||
unique_id="test",
|
||||
),
|
||||
config_entries.ConfigSubentryData(
|
||||
data={},
|
||||
subentry_id="mock-subentry-id-1-2",
|
||||
title="Mock title",
|
||||
unique_id="test",
|
||||
),
|
||||
)
|
||||
)
|
||||
config_entry_1.add_to_hass(hass)
|
||||
@ -214,7 +208,7 @@ async def test_multiple_config_subentries(
|
||||
model="model",
|
||||
)
|
||||
assert entry.config_entries == {config_entry_1.entry_id}
|
||||
assert entry.config_subentries == {config_entry_1.entry_id: {None}}
|
||||
assert entry.config_subentries == {config_entry_1.entry_id: None}
|
||||
entry_id = entry.id
|
||||
|
||||
entry = device_registry.async_get_or_create(
|
||||
@ -227,35 +221,17 @@ async def test_multiple_config_subentries(
|
||||
)
|
||||
assert entry.id == entry_id
|
||||
assert entry.config_entries == {config_entry_1.entry_id}
|
||||
assert entry.config_subentries == {config_entry_1.entry_id: {None}}
|
||||
assert entry.config_subentries == {config_entry_1.entry_id: None}
|
||||
|
||||
entry = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_1.entry_id,
|
||||
config_subentry_id="mock-subentry-id-1-1",
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
identifiers={("bridgeid", "0123")},
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
)
|
||||
assert entry.id == entry_id
|
||||
assert entry.config_entries == {config_entry_1.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {None, "mock-subentry-id-1-1"}
|
||||
}
|
||||
|
||||
entry = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_1.entry_id,
|
||||
config_subentry_id="mock-subentry-id-1-2",
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
identifiers={("bridgeid", "0123")},
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
)
|
||||
assert entry.id == entry_id
|
||||
assert entry.config_entries == {config_entry_1.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {None, "mock-subentry-id-1-1", "mock-subentry-id-1-2"}
|
||||
}
|
||||
with pytest.raises(HomeAssistantError):
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_1.entry_id,
|
||||
config_subentry_id="mock-subentry-id-1-1",
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
identifiers={("bridgeid", "0123")},
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
)
|
||||
|
||||
entry = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_2.entry_id,
|
||||
@ -268,8 +244,8 @@ async def test_multiple_config_subentries(
|
||||
assert entry.id == entry_id
|
||||
assert entry.config_entries == {config_entry_1.entry_id, config_entry_2.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {None, "mock-subentry-id-1-1", "mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_1.entry_id: None,
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
}
|
||||
|
||||
|
||||
@ -291,7 +267,7 @@ async def test_loading_from_storage(
|
||||
{
|
||||
"area_id": "12345A",
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"config_subentries": {mock_config_entry.entry_id: [None]},
|
||||
"config_subentries": {mock_config_entry.entry_id: None},
|
||||
"configuration_url": "https://example.com/config",
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": created_at,
|
||||
@ -316,7 +292,7 @@ async def test_loading_from_storage(
|
||||
"deleted_devices": [
|
||||
{
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"config_subentries": {mock_config_entry.entry_id: [None]},
|
||||
"config_subentries": {mock_config_entry.entry_id: None},
|
||||
"connections": [["Zigbee", "23.45.67.89.01"]],
|
||||
"created_at": created_at,
|
||||
"id": "bcdefghijklmn",
|
||||
@ -335,7 +311,7 @@ async def test_loading_from_storage(
|
||||
|
||||
assert registry.deleted_devices["bcdefghijklmn"] == dr.DeletedDeviceEntry(
|
||||
config_entries={mock_config_entry.entry_id},
|
||||
config_subentries={mock_config_entry.entry_id: {None}},
|
||||
config_subentries={mock_config_entry.entry_id: None},
|
||||
connections={("Zigbee", "23.45.67.89.01")},
|
||||
created_at=datetime.fromisoformat(created_at),
|
||||
id="bcdefghijklmn",
|
||||
@ -354,7 +330,7 @@ async def test_loading_from_storage(
|
||||
assert entry == dr.DeviceEntry(
|
||||
area_id="12345A",
|
||||
config_entries={mock_config_entry.entry_id},
|
||||
config_subentries={mock_config_entry.entry_id: {None}},
|
||||
config_subentries={mock_config_entry.entry_id: None},
|
||||
configuration_url="https://example.com/config",
|
||||
connections={("Zigbee", "01.23.45.67.89")},
|
||||
created_at=datetime.fromisoformat(created_at),
|
||||
@ -389,7 +365,7 @@ async def test_loading_from_storage(
|
||||
)
|
||||
assert entry == dr.DeviceEntry(
|
||||
config_entries={mock_config_entry.entry_id},
|
||||
config_subentries={mock_config_entry.entry_id: {None}},
|
||||
config_subentries={mock_config_entry.entry_id: None},
|
||||
connections={("Zigbee", "23.45.67.89.01")},
|
||||
created_at=datetime.fromisoformat(created_at),
|
||||
id="bcdefghijklmn",
|
||||
@ -489,7 +465,7 @@ async def test_migration_from_1_1(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"config_subentries": {mock_config_entry.entry_id: [None]},
|
||||
"config_subentries": {mock_config_entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -513,7 +489,7 @@ async def test_migration_from_1_1(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": ["234567"],
|
||||
"config_subentries": {"234567": [None]},
|
||||
"config_subentries": {"234567": None},
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -538,7 +514,7 @@ async def test_migration_from_1_1(
|
||||
"deleted_devices": [
|
||||
{
|
||||
"config_entries": ["123456"],
|
||||
"config_subentries": {"123456": [None]},
|
||||
"config_subentries": {"123456": None},
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"id": "deletedid",
|
||||
@ -636,7 +612,7 @@ async def test_migration_from_1_2(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"config_subentries": {mock_config_entry.entry_id: [None]},
|
||||
"config_subentries": {mock_config_entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -660,7 +636,7 @@ async def test_migration_from_1_2(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": ["234567"],
|
||||
"config_subentries": {"234567": [None]},
|
||||
"config_subentries": {"234567": None},
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -772,7 +748,7 @@ async def test_migration_fom_1_3(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"config_subentries": {mock_config_entry.entry_id: [None]},
|
||||
"config_subentries": {mock_config_entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -796,7 +772,7 @@ async def test_migration_fom_1_3(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": ["234567"],
|
||||
"config_subentries": {"234567": [None]},
|
||||
"config_subentries": {"234567": None},
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -910,7 +886,7 @@ async def test_migration_from_1_4(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"config_subentries": {mock_config_entry.entry_id: [None]},
|
||||
"config_subentries": {mock_config_entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -934,7 +910,7 @@ async def test_migration_from_1_4(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": ["234567"],
|
||||
"config_subentries": {"234567": [None]},
|
||||
"config_subentries": {"234567": None},
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -1050,7 +1026,7 @@ async def test_migration_from_1_5(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"config_subentries": {mock_config_entry.entry_id: [None]},
|
||||
"config_subentries": {mock_config_entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -1074,7 +1050,7 @@ async def test_migration_from_1_5(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": ["234567"],
|
||||
"config_subentries": {"234567": [None]},
|
||||
"config_subentries": {"234567": None},
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -1192,7 +1168,7 @@ async def test_migration_from_1_6(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"config_subentries": {mock_config_entry.entry_id: [None]},
|
||||
"config_subentries": {mock_config_entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -1216,7 +1192,7 @@ async def test_migration_from_1_6(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": ["234567"],
|
||||
"config_subentries": {"234567": [None]},
|
||||
"config_subentries": {"234567": None},
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -1336,7 +1312,7 @@ async def test_migration_from_1_7(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"config_subentries": {mock_config_entry.entry_id: [None]},
|
||||
"config_subentries": {mock_config_entry.entry_id: None},
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -1360,7 +1336,7 @@ async def test_migration_from_1_7(
|
||||
{
|
||||
"area_id": None,
|
||||
"config_entries": ["234567"],
|
||||
"config_subentries": {"234567": [None]},
|
||||
"config_subentries": {"234567": None},
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
@ -1445,7 +1421,7 @@ async def test_removing_config_entries(
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id},
|
||||
"config_subentries": {config_entry_1.entry_id: {None}},
|
||||
"config_subentries": {config_entry_1.entry_id: None},
|
||||
},
|
||||
}
|
||||
assert update_events[2].data == {
|
||||
@ -1458,8 +1434,8 @@ async def test_removing_config_entries(
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id, config_entry_2.entry_id},
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {None},
|
||||
config_entry_2.entry_id: {None},
|
||||
config_entry_1.entry_id: None,
|
||||
config_entry_2.entry_id: None,
|
||||
},
|
||||
"primary_config_entry": config_entry_1.entry_id,
|
||||
},
|
||||
@ -1525,7 +1501,7 @@ async def test_deleted_device_removing_config_entries(
|
||||
"device_id": entry2.id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id},
|
||||
"config_subentries": {config_entry_1.entry_id: {None}},
|
||||
"config_subentries": {config_entry_1.entry_id: None},
|
||||
},
|
||||
}
|
||||
assert update_events[2].data == {
|
||||
@ -1593,12 +1569,6 @@ async def test_removing_config_subentries(
|
||||
title="Mock title",
|
||||
unique_id="test",
|
||||
),
|
||||
config_entries.ConfigSubentryData(
|
||||
data={},
|
||||
subentry_id="mock-subentry-id-1-2",
|
||||
title="Mock title",
|
||||
unique_id="test",
|
||||
),
|
||||
)
|
||||
)
|
||||
config_entry_1.add_to_hass(hass)
|
||||
@ -1621,22 +1591,6 @@ async def test_removing_config_subentries(
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
)
|
||||
entry2 = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_1.entry_id,
|
||||
config_subentry_id="mock-subentry-id-1-1",
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
identifiers={("bridgeid", "0123")},
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
)
|
||||
entry3 = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_1.entry_id,
|
||||
config_subentry_id="mock-subentry-id-1-2",
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
identifiers={("bridgeid", "0123")},
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
)
|
||||
entry4 = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_2.entry_id,
|
||||
config_subentry_id="mock-subentry-id-2-1",
|
||||
@ -1647,21 +1601,11 @@ async def test_removing_config_subentries(
|
||||
)
|
||||
|
||||
assert len(device_registry.devices) == 1
|
||||
assert entry.id == entry2.id
|
||||
assert entry.id == entry3.id
|
||||
assert entry.id == entry4.id
|
||||
assert entry4.config_entries == {config_entry_1.entry_id, config_entry_2.entry_id}
|
||||
assert entry4.config_subentries == {
|
||||
config_entry_1.entry_id: {None, "mock-subentry-id-1-1", "mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
}
|
||||
|
||||
device_registry.async_clear_config_subentry(config_entry_1.entry_id, None)
|
||||
entry = device_registry.async_get_device(identifiers={("bridgeid", "0123")})
|
||||
assert entry.config_entries == {config_entry_1.entry_id, config_entry_2.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {"mock-subentry-id-1-1", "mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_1.entry_id: None,
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
}
|
||||
|
||||
device_registry.async_clear_config_subentry(
|
||||
@ -1670,17 +1614,15 @@ async def test_removing_config_subentries(
|
||||
entry = device_registry.async_get_device(identifiers={("bridgeid", "0123")})
|
||||
assert entry.config_entries == {config_entry_1.entry_id, config_entry_2.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {"mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_1.entry_id: None,
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
}
|
||||
|
||||
device_registry.async_clear_config_subentry(
|
||||
config_entry_1.entry_id, "mock-subentry-id-1-2"
|
||||
)
|
||||
device_registry.async_clear_config_subentry(config_entry_1.entry_id, None)
|
||||
entry = device_registry.async_get_device(identifiers={("bridgeid", "0123")})
|
||||
assert entry.config_entries == {config_entry_2.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"}
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
}
|
||||
|
||||
device_registry.async_clear_config_subentry(
|
||||
@ -1691,7 +1633,7 @@ async def test_removing_config_subentries(
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(update_events) == 8
|
||||
assert len(update_events) == 4
|
||||
assert update_events[0].data == {
|
||||
"action": "create",
|
||||
"device_id": entry.id,
|
||||
@ -1700,81 +1642,24 @@ async def test_removing_config_subentries(
|
||||
"action": "update",
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_subentries": {config_entry_1.entry_id: {None}},
|
||||
"config_entries": {config_entry_1.entry_id},
|
||||
"config_subentries": {config_entry_1.entry_id: None},
|
||||
"identifiers": {("bridgeid", "0123")},
|
||||
},
|
||||
}
|
||||
assert update_events[2].data == {
|
||||
"action": "update",
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {None, "mock-subentry-id-1-1"}
|
||||
},
|
||||
},
|
||||
}
|
||||
assert update_events[3].data == {
|
||||
"action": "update",
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id},
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {
|
||||
None,
|
||||
"mock-subentry-id-1-1",
|
||||
"mock-subentry-id-1-2",
|
||||
}
|
||||
},
|
||||
"identifiers": {("bridgeid", "0123")},
|
||||
},
|
||||
}
|
||||
assert update_events[4].data == {
|
||||
"action": "update",
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {
|
||||
None,
|
||||
"mock-subentry-id-1-1",
|
||||
"mock-subentry-id-1-2",
|
||||
},
|
||||
config_entry_2.entry_id: {
|
||||
"mock-subentry-id-2-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert update_events[5].data == {
|
||||
"action": "update",
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {
|
||||
"mock-subentry-id-1-1",
|
||||
"mock-subentry-id-1-2",
|
||||
},
|
||||
config_entry_2.entry_id: {
|
||||
"mock-subentry-id-2-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert update_events[6].data == {
|
||||
"action": "update",
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id, config_entry_2.entry_id},
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {
|
||||
"mock-subentry-id-1-2",
|
||||
},
|
||||
config_entry_2.entry_id: {
|
||||
"mock-subentry-id-2-1",
|
||||
},
|
||||
config_entry_1.entry_id: None,
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
},
|
||||
"primary_config_entry": config_entry_1.entry_id,
|
||||
},
|
||||
}
|
||||
assert update_events[7].data == {
|
||||
assert update_events[3].data == {
|
||||
"action": "remove",
|
||||
"device_id": entry.id,
|
||||
}
|
||||
@ -1785,22 +1670,7 @@ async def test_deleted_device_removing_config_subentries(
|
||||
) -> None:
|
||||
"""Make sure we do not get duplicate entries."""
|
||||
update_events = async_capture_events(hass, dr.EVENT_DEVICE_REGISTRY_UPDATED)
|
||||
config_entry_1 = MockConfigEntry(
|
||||
subentries_data=(
|
||||
config_entries.ConfigSubentryData(
|
||||
data={},
|
||||
subentry_id="mock-subentry-id-1-1",
|
||||
title="Mock title",
|
||||
unique_id="test",
|
||||
),
|
||||
config_entries.ConfigSubentryData(
|
||||
data={},
|
||||
subentry_id="mock-subentry-id-1-2",
|
||||
title="Mock title",
|
||||
unique_id="test",
|
||||
),
|
||||
)
|
||||
)
|
||||
config_entry_1 = MockConfigEntry()
|
||||
config_entry_1.add_to_hass(hass)
|
||||
config_entry_2 = MockConfigEntry(
|
||||
subentries_data=(
|
||||
@ -1821,22 +1691,6 @@ async def test_deleted_device_removing_config_subentries(
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
)
|
||||
entry2 = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_1.entry_id,
|
||||
config_subentry_id="mock-subentry-id-1-1",
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
identifiers={("bridgeid", "0123")},
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
)
|
||||
entry3 = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_1.entry_id,
|
||||
config_subentry_id="mock-subentry-id-1-2",
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
identifiers={("bridgeid", "0123")},
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
)
|
||||
entry4 = device_registry.async_get_or_create(
|
||||
config_entry_id=config_entry_2.entry_id,
|
||||
config_subentry_id="mock-subentry-id-2-1",
|
||||
@ -1848,13 +1702,11 @@ async def test_deleted_device_removing_config_subentries(
|
||||
|
||||
assert len(device_registry.devices) == 1
|
||||
assert len(device_registry.deleted_devices) == 0
|
||||
assert entry.id == entry2.id
|
||||
assert entry.id == entry3.id
|
||||
assert entry.id == entry4.id
|
||||
assert entry4.config_entries == {config_entry_1.entry_id, config_entry_2.entry_id}
|
||||
assert entry4.config_subentries == {
|
||||
config_entry_1.entry_id: {None, "mock-subentry-id-1-1", "mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_1.entry_id: None,
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
}
|
||||
|
||||
device_registry.async_remove_device(entry.id)
|
||||
@ -1864,85 +1716,47 @@ async def test_deleted_device_removing_config_subentries(
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(update_events) == 5
|
||||
assert len(update_events) == 3
|
||||
assert update_events[0].data == {
|
||||
"action": "create",
|
||||
"device_id": entry.id,
|
||||
}
|
||||
assert update_events[1].data == {
|
||||
"action": "update",
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_subentries": {config_entry_1.entry_id: {None}},
|
||||
},
|
||||
}
|
||||
assert update_events[2].data == {
|
||||
"action": "update",
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {None, "mock-subentry-id-1-1"}
|
||||
},
|
||||
},
|
||||
}
|
||||
assert update_events[3].data == {
|
||||
"action": "update",
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id},
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {
|
||||
None,
|
||||
"mock-subentry-id-1-1",
|
||||
"mock-subentry-id-1-2",
|
||||
}
|
||||
},
|
||||
"config_subentries": {config_entry_1.entry_id: None},
|
||||
"identifiers": {("bridgeid", "0123")},
|
||||
},
|
||||
}
|
||||
assert update_events[4].data == {
|
||||
assert update_events[2].data == {
|
||||
"action": "remove",
|
||||
"device_id": entry.id,
|
||||
}
|
||||
|
||||
device_registry.async_clear_config_subentry(config_entry_1.entry_id, None)
|
||||
entry = device_registry.deleted_devices.get_entry({("bridgeid", "0123")}, None)
|
||||
assert entry.config_entries == {config_entry_1.entry_id, config_entry_2.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {"mock-subentry-id-1-1", "mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_1.entry_id: None,
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
}
|
||||
assert entry.orphaned_timestamp is None
|
||||
|
||||
device_registry.async_clear_config_subentry(
|
||||
config_entry_1.entry_id, "mock-subentry-id-1-1"
|
||||
)
|
||||
device_registry.async_clear_config_subentry(config_entry_1.entry_id, None)
|
||||
entry = device_registry.deleted_devices.get_entry({("bridgeid", "0123")}, None)
|
||||
assert entry.config_entries == {config_entry_1.entry_id, config_entry_2.entry_id}
|
||||
assert entry.config_entries == {config_entry_2.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {"mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
}
|
||||
assert entry.orphaned_timestamp is None
|
||||
|
||||
# Remove the same subentry again
|
||||
device_registry.async_clear_config_subentry(
|
||||
config_entry_1.entry_id, "mock-subentry-id-1-1"
|
||||
)
|
||||
device_registry.async_clear_config_subentry(config_entry_1.entry_id, None)
|
||||
assert (
|
||||
device_registry.deleted_devices.get_entry({("bridgeid", "0123")}, None) is entry
|
||||
)
|
||||
|
||||
device_registry.async_clear_config_subentry(
|
||||
config_entry_1.entry_id, "mock-subentry-id-1-2"
|
||||
)
|
||||
entry = device_registry.deleted_devices.get_entry({("bridgeid", "0123")}, None)
|
||||
assert entry.config_entries == {config_entry_2.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"}
|
||||
}
|
||||
assert entry.orphaned_timestamp is None
|
||||
|
||||
device_registry.async_clear_config_subentry(
|
||||
config_entry_2.entry_id, "mock-subentry-id-2-1"
|
||||
)
|
||||
@ -1953,7 +1767,7 @@ async def test_deleted_device_removing_config_subentries(
|
||||
|
||||
# No event when a deleted device is purged
|
||||
await hass.async_block_till_done()
|
||||
assert len(update_events) == 5
|
||||
assert len(update_events) == 3
|
||||
|
||||
# Re-add, expect to keep the device id
|
||||
restored_entry = device_registry.async_get_or_create(
|
||||
@ -2375,7 +2189,7 @@ async def test_update(
|
||||
assert updated_entry == dr.DeviceEntry(
|
||||
area_id="12345A",
|
||||
config_entries={mock_config_entry.entry_id},
|
||||
config_subentries={mock_config_entry.entry_id: {None}},
|
||||
config_subentries={mock_config_entry.entry_id: None},
|
||||
configuration_url="https://example.com/config",
|
||||
connections={("mac", "65:43:21:fe:dc:ba")},
|
||||
created_at=created_at,
|
||||
@ -2632,7 +2446,7 @@ async def test_update_remove_config_entries(
|
||||
"device_id": entry2.id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id},
|
||||
"config_subentries": {config_entry_1.entry_id: {None}},
|
||||
"config_subentries": {config_entry_1.entry_id: None},
|
||||
},
|
||||
}
|
||||
assert update_events[2].data == {
|
||||
@ -2645,8 +2459,8 @@ async def test_update_remove_config_entries(
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id, config_entry_2.entry_id},
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {None},
|
||||
config_entry_2.entry_id: {None},
|
||||
config_entry_1.entry_id: None,
|
||||
config_entry_2.entry_id: None,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -2660,9 +2474,9 @@ async def test_update_remove_config_entries(
|
||||
config_entry_3.entry_id,
|
||||
},
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {None},
|
||||
config_entry_2.entry_id: {None},
|
||||
config_entry_3.entry_id: {None},
|
||||
config_entry_1.entry_id: None,
|
||||
config_entry_2.entry_id: None,
|
||||
config_entry_3.entry_id: None,
|
||||
},
|
||||
"primary_config_entry": config_entry_1.entry_id,
|
||||
},
|
||||
@ -2673,8 +2487,8 @@ async def test_update_remove_config_entries(
|
||||
"changes": {
|
||||
"config_entries": {config_entry_2.entry_id, config_entry_3.entry_id},
|
||||
"config_subentries": {
|
||||
config_entry_2.entry_id: {None},
|
||||
config_entry_3.entry_id: {None},
|
||||
config_entry_2.entry_id: None,
|
||||
config_entry_3.entry_id: None,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -2697,12 +2511,6 @@ async def test_update_remove_config_subentries(
|
||||
title="Mock title",
|
||||
unique_id="test",
|
||||
),
|
||||
config_entries.ConfigSubentryData(
|
||||
data={},
|
||||
subentry_id="mock-subentry-id-1-2",
|
||||
title="Mock title",
|
||||
unique_id="test",
|
||||
),
|
||||
)
|
||||
)
|
||||
config_entry_1.add_to_hass(hass)
|
||||
@ -2730,26 +2538,14 @@ async def test_update_remove_config_subentries(
|
||||
)
|
||||
entry_id = entry.id
|
||||
assert entry.config_entries == {config_entry_1.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {"mock-subentry-id-1-1"}
|
||||
}
|
||||
|
||||
entry = device_registry.async_update_device(
|
||||
entry_id,
|
||||
add_config_entry_id=config_entry_1.entry_id,
|
||||
add_config_subentry_id="mock-subentry-id-1-2",
|
||||
)
|
||||
assert entry.config_entries == {config_entry_1.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {"mock-subentry-id-1-1", "mock-subentry-id-1-2"}
|
||||
}
|
||||
assert entry.config_subentries == {config_entry_1.entry_id: "mock-subentry-id-1-1"}
|
||||
|
||||
# Try adding the same subentry again
|
||||
assert (
|
||||
device_registry.async_update_device(
|
||||
entry_id,
|
||||
add_config_entry_id=config_entry_1.entry_id,
|
||||
add_config_subentry_id="mock-subentry-id-1-2",
|
||||
add_config_subentry_id="mock-subentry-id-1-1",
|
||||
)
|
||||
is entry
|
||||
)
|
||||
@ -2761,8 +2557,8 @@ async def test_update_remove_config_subentries(
|
||||
)
|
||||
assert entry.config_entries == {config_entry_1.entry_id, config_entry_2.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {"mock-subentry-id-1-1", "mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_1.entry_id: "mock-subentry-id-1-1",
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
}
|
||||
|
||||
entry = device_registry.async_update_device(
|
||||
@ -2776,9 +2572,9 @@ async def test_update_remove_config_subentries(
|
||||
config_entry_3.entry_id,
|
||||
}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {"mock-subentry-id-1-1", "mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_3.entry_id: {None},
|
||||
config_entry_1.entry_id: "mock-subentry-id-1-1",
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
config_entry_3.entry_id: None,
|
||||
}
|
||||
|
||||
# Try to add a subentry without specifying entry
|
||||
@ -2799,31 +2595,19 @@ async def test_update_remove_config_subentries(
|
||||
add_config_subentry_id="blabla",
|
||||
)
|
||||
|
||||
# Try to remove a subentry without specifying entry
|
||||
with pytest.raises(
|
||||
HomeAssistantError,
|
||||
match="Can't remove config subentry without specifying config entry",
|
||||
):
|
||||
device_registry.async_update_device(
|
||||
entry_id, remove_config_subentry_id="blabla"
|
||||
)
|
||||
|
||||
assert len(device_registry.devices) == 1
|
||||
|
||||
entry = device_registry.async_update_device(
|
||||
entry_id,
|
||||
remove_config_entry_id=config_entry_1.entry_id,
|
||||
remove_config_subentry_id="mock-subentry-id-1-1",
|
||||
)
|
||||
assert entry.config_entries == {
|
||||
config_entry_1.entry_id,
|
||||
config_entry_2.entry_id,
|
||||
config_entry_3.entry_id,
|
||||
}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_1.entry_id: {"mock-subentry-id-1-2"},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_3.entry_id: {None},
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
config_entry_3.entry_id: None,
|
||||
}
|
||||
|
||||
# Try removing the same subentry again
|
||||
@ -2831,42 +2615,28 @@ async def test_update_remove_config_subentries(
|
||||
device_registry.async_update_device(
|
||||
entry_id,
|
||||
remove_config_entry_id=config_entry_1.entry_id,
|
||||
remove_config_subentry_id="mock-subentry-id-1-1",
|
||||
)
|
||||
is entry
|
||||
)
|
||||
|
||||
entry = device_registry.async_update_device(
|
||||
entry_id,
|
||||
remove_config_entry_id=config_entry_1.entry_id,
|
||||
remove_config_subentry_id="mock-subentry-id-1-2",
|
||||
)
|
||||
assert entry.config_entries == {config_entry_2.entry_id, config_entry_3.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_3.entry_id: {None},
|
||||
}
|
||||
|
||||
entry = device_registry.async_update_device(
|
||||
entry_id,
|
||||
remove_config_entry_id=config_entry_2.entry_id,
|
||||
remove_config_subentry_id="mock-subentry-id-2-1",
|
||||
)
|
||||
assert entry.config_entries == {config_entry_3.entry_id}
|
||||
assert entry.config_subentries == {
|
||||
config_entry_3.entry_id: {None},
|
||||
config_entry_3.entry_id: None,
|
||||
}
|
||||
|
||||
entry = device_registry.async_update_device(
|
||||
entry_id,
|
||||
remove_config_entry_id=config_entry_3.entry_id,
|
||||
remove_config_subentry_id=None,
|
||||
)
|
||||
assert entry is None
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(update_events) == 8
|
||||
assert len(update_events) == 6
|
||||
assert update_events[0].data == {
|
||||
"action": "create",
|
||||
"device_id": entry_id,
|
||||
@ -2875,51 +2645,22 @@ async def test_update_remove_config_subentries(
|
||||
"action": "update",
|
||||
"device_id": entry_id,
|
||||
"changes": {
|
||||
"config_subentries": {config_entry_1.entry_id: {"mock-subentry-id-1-1"}},
|
||||
"config_entries": {config_entry_1.entry_id},
|
||||
"config_subentries": {config_entry_1.entry_id: "mock-subentry-id-1-1"},
|
||||
},
|
||||
}
|
||||
assert update_events[2].data == {
|
||||
"action": "update",
|
||||
"device_id": entry_id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id},
|
||||
"config_entries": {config_entry_1.entry_id, config_entry_2.entry_id},
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {
|
||||
"mock-subentry-id-1-1",
|
||||
"mock-subentry-id-1-2",
|
||||
}
|
||||
config_entry_1.entry_id: "mock-subentry-id-1-1",
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
},
|
||||
},
|
||||
}
|
||||
assert update_events[3].data == {
|
||||
"action": "update",
|
||||
"device_id": entry_id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id, config_entry_2.entry_id},
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {
|
||||
"mock-subentry-id-1-1",
|
||||
"mock-subentry-id-1-2",
|
||||
},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert update_events[4].data == {
|
||||
"action": "update",
|
||||
"device_id": entry_id,
|
||||
"changes": {
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {
|
||||
"mock-subentry-id-1-1",
|
||||
"mock-subentry-id-1-2",
|
||||
},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_3.entry_id: {None},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert update_events[5].data == {
|
||||
"action": "update",
|
||||
"device_id": entry_id,
|
||||
"changes": {
|
||||
@ -2929,27 +2670,25 @@ async def test_update_remove_config_subentries(
|
||||
config_entry_3.entry_id,
|
||||
},
|
||||
"config_subentries": {
|
||||
config_entry_1.entry_id: {
|
||||
"mock-subentry-id-1-2",
|
||||
},
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_3.entry_id: {None},
|
||||
config_entry_1.entry_id: "mock-subentry-id-1-1",
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
config_entry_3.entry_id: None,
|
||||
},
|
||||
"primary_config_entry": config_entry_1.entry_id,
|
||||
},
|
||||
}
|
||||
assert update_events[6].data == {
|
||||
assert update_events[4].data == {
|
||||
"action": "update",
|
||||
"device_id": entry_id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_2.entry_id, config_entry_3.entry_id},
|
||||
"config_subentries": {
|
||||
config_entry_2.entry_id: {"mock-subentry-id-2-1"},
|
||||
config_entry_3.entry_id: {None},
|
||||
config_entry_2.entry_id: "mock-subentry-id-2-1",
|
||||
config_entry_3.entry_id: None,
|
||||
},
|
||||
},
|
||||
}
|
||||
assert update_events[7].data == {
|
||||
assert update_events[5].data == {
|
||||
"action": "remove",
|
||||
"device_id": entry_id,
|
||||
}
|
||||
@ -3361,7 +3100,7 @@ async def test_restore_shared_device(
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_1.entry_id},
|
||||
"config_subentries": {config_entry_1.entry_id: {None}},
|
||||
"config_subentries": {config_entry_1.entry_id: None},
|
||||
"identifiers": {("entry_123", "0123")},
|
||||
},
|
||||
}
|
||||
@ -3386,7 +3125,7 @@ async def test_restore_shared_device(
|
||||
"device_id": entry.id,
|
||||
"changes": {
|
||||
"config_entries": {config_entry_2.entry_id},
|
||||
"config_subentries": {config_entry_2.entry_id: {None}},
|
||||
"config_subentries": {config_entry_2.entry_id: None},
|
||||
"identifiers": {("entry_234", "2345")},
|
||||
},
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user