Improve error handling in websocket_update_device (#179709)

This commit is contained in:
Erik Montnemery
2026-08-21 16:11:24 +02:00
committed by GitHub
parent cde0100c33
commit 32a4846428
2 changed files with 95 additions and 1 deletions
@@ -178,8 +178,26 @@ def websocket_update_device(
# Convert labels to a set
msg["labels"] = set(msg["labels"])
device_id = msg["device_id"]
# A composite device id has no single underlying device to update; reject it.
if (
registry.async_get(
device_id, include_main_devices=False, include_child_devices=False
)
is not None
):
connection.send_error(
msg_id, websocket_api.ERR_NOT_ALLOWED, "Cannot update a composite device"
)
return
if (
device := registry.async_get(device_id, include_composite_devices=False)
) is None:
connection.send_error(msg_id, websocket_api.ERR_NOT_FOUND, "Device not found")
return
entry: dr.AnyDeviceEntry | None
device = registry.async_get(msg["device_id"], include_composite_devices=False)
if isinstance(device, dr.ChildDeviceEntry):
entry = registry.async_update_child_device(**msg)
else:
@@ -388,6 +388,82 @@ async def test_update_device_labels(
assert getattr(device, key) == value
async def test_update_device_unknown_device(
hass: HomeAssistant,
client: MockHAClientWebSocket,
) -> None:
"""Test updating an unknown device returns an error."""
await client.send_json_auto_id(
{
"type": "config/device_registry/update",
"device_id": "does_not_exist",
"name_by_user": "Test Friendly Name",
}
)
msg = await client.receive_json()
assert not msg["success"]
assert msg["error"]["code"] == "not_found"
assert msg["error"]["message"] == "Device not found"
@pytest.mark.parametrize("load_registries", [False])
async def test_update_device_composite(
hass: HomeAssistant,
client: MockHAClientWebSocket,
hass_storage: dict[str, Any],
) -> None:
"""Test updating a pre-migration composite device id is rejected."""
entry_1 = MockConfigEntry()
entry_1.add_to_hass(hass)
entry_2 = MockConfigEntry()
entry_2.add_to_hass(hass)
composite_id = "compositea000000000000000000000"
hass_storage[dr.STORAGE_KEY] = {
"version": 1,
"minor_version": 12,
"key": dr.STORAGE_KEY,
"data": {
"devices": [
# Composite spanning two config entries; splitting it on load removes
# the composite device, so composite_id no longer refers to a device
_storage_device_v1_12(
composite_id,
[entry_1.entry_id, entry_2.entry_id],
entry_1.entry_id,
"a",
),
],
"deleted_devices": [],
},
}
dr.async_setup(hass)
await dr.async_load(hass)
# pylint: disable-next=home-assistant-tests-registry-fixtures
registry = dr.async_get(hass)
assert registry.async_get(composite_id) is not None
assert registry.async_get(composite_id, include_composite_devices=False) is None
await client.send_json_auto_id(
{
"type": "config/device_registry/update",
"device_id": composite_id,
"name_by_user": "Test Friendly Name",
}
)
msg = await client.receive_json()
assert not msg["success"]
assert msg["error"]["code"] == "not_allowed"
assert msg["error"]["message"] == "Cannot update a composite device"
# The update was not fanned out to the underlying split devices
for split in registry.async_get_devices_for_composite_device_id(composite_id):
assert split.name_by_user is None
_DEPRECATION_WARNING = (
"The websocket command config/device_registry/remove_config_entry is "
"deprecated and will be removed in Home Assistant 2027.9"