Include changes in EVENT_DEVICE_REGISTRY_UPDATED (#66641)

This commit is contained in:
Erik Montnemery 2022-02-16 12:29:08 +01:00 committed by GitHub
parent 21f2c664d9
commit dcb3fc49c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 119 additions and 22 deletions

View File

@ -11,7 +11,7 @@ import attr
from homeassistant.backports.enum import StrEnum from homeassistant.backports.enum import StrEnum
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
from homeassistant.core import Event, HomeAssistant, callback from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import RequiredParameterMissing from homeassistant.exceptions import HomeAssistantError, RequiredParameterMissing
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
import homeassistant.util.uuid as uuid_util import homeassistant.util.uuid as uuid_util
@ -420,10 +420,14 @@ class DeviceRegistry:
"""Update device attributes.""" """Update device attributes."""
old = self.devices[device_id] old = self.devices[device_id]
changes: dict[str, Any] = {} new_values: dict[str, Any] = {} # Dict with new key/value pairs
old_values: dict[str, Any] = {} # Dict with old key/value pairs
config_entries = old.config_entries config_entries = old.config_entries
if merge_identifiers is not UNDEFINED and new_identifiers is not UNDEFINED:
raise HomeAssistantError()
if isinstance(disabled_by, str) and not isinstance( if isinstance(disabled_by, str) and not isinstance(
disabled_by, DeviceEntryDisabler disabled_by, DeviceEntryDisabler
): ):
@ -462,7 +466,8 @@ class DeviceRegistry:
config_entries = config_entries - {remove_config_entry_id} config_entries = config_entries - {remove_config_entry_id}
if config_entries != old.config_entries: if config_entries != old.config_entries:
changes["config_entries"] = config_entries new_values["config_entries"] = config_entries
old_values["config_entries"] = old.config_entries
for attr_name, setvalue in ( for attr_name, setvalue in (
("connections", merge_connections), ("connections", merge_connections),
@ -471,10 +476,12 @@ class DeviceRegistry:
old_value = getattr(old, attr_name) old_value = getattr(old, attr_name)
# If not undefined, check if `value` contains new items. # If not undefined, check if `value` contains new items.
if setvalue is not UNDEFINED and not setvalue.issubset(old_value): if setvalue is not UNDEFINED and not setvalue.issubset(old_value):
changes[attr_name] = old_value | setvalue new_values[attr_name] = old_value | setvalue
old_values[attr_name] = old_value
if new_identifiers is not UNDEFINED: if new_identifiers is not UNDEFINED:
changes["identifiers"] = new_identifiers new_values["identifiers"] = new_identifiers
old_values["identifiers"] = old.identifiers
for attr_name, value in ( for attr_name, value in (
("configuration_url", configuration_url), ("configuration_url", configuration_url),
@ -491,25 +498,27 @@ class DeviceRegistry:
("via_device_id", via_device_id), ("via_device_id", via_device_id),
): ):
if value is not UNDEFINED and value != getattr(old, attr_name): if value is not UNDEFINED and value != getattr(old, attr_name):
changes[attr_name] = value new_values[attr_name] = value
old_values[attr_name] = getattr(old, attr_name)
if old.is_new: if old.is_new:
changes["is_new"] = False new_values["is_new"] = False
if not changes: if not new_values:
return old return old
new = attr.evolve(old, **changes) new = attr.evolve(old, **new_values)
self._update_device(old, new) self._update_device(old, new)
self.async_schedule_save() self.async_schedule_save()
self.hass.bus.async_fire( data: dict[str, Any] = {
EVENT_DEVICE_REGISTRY_UPDATED, "action": "create" if old.is_new else "update",
{
"action": "create" if "is_new" in changes else "update",
"device_id": new.id, "device_id": new.id,
}, }
) if not old.is_new:
data["changes"] = old_values
self.hass.bus.async_fire(EVENT_DEVICE_REGISTRY_UPDATED, data)
return new return new

View File

@ -96,8 +96,12 @@ async def test_get_or_create_returns_same_entry(
assert len(update_events) == 2 assert len(update_events) == 2
assert update_events[0]["action"] == "create" assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "update" assert update_events[1]["action"] == "update"
assert update_events[1]["device_id"] == entry.id assert update_events[1]["device_id"] == entry.id
assert update_events[1]["changes"] == {
"connections": {("mac", "12:34:56:ab:cd:ef")}
}
async def test_requirement_for_identifier_or_connection(registry): async def test_requirement_for_identifier_or_connection(registry):
@ -518,14 +522,19 @@ async def test_removing_config_entries(hass, registry, update_events):
assert len(update_events) == 5 assert len(update_events) == 5
assert update_events[0]["action"] == "create" assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "update" assert update_events[1]["action"] == "update"
assert update_events[1]["device_id"] == entry2.id assert update_events[1]["device_id"] == entry2.id
assert update_events[1]["changes"] == {"config_entries": {"123"}}
assert update_events[2]["action"] == "create" assert update_events[2]["action"] == "create"
assert update_events[2]["device_id"] == entry3.id assert update_events[2]["device_id"] == entry3.id
assert "changes" not in update_events[2]
assert update_events[3]["action"] == "update" assert update_events[3]["action"] == "update"
assert update_events[3]["device_id"] == entry.id assert update_events[3]["device_id"] == entry.id
assert update_events[3]["changes"] == {"config_entries": {"456", "123"}}
assert update_events[4]["action"] == "remove" assert update_events[4]["action"] == "remove"
assert update_events[4]["device_id"] == entry3.id assert update_events[4]["device_id"] == entry3.id
assert "changes" not in update_events[4]
async def test_deleted_device_removing_config_entries(hass, registry, update_events): async def test_deleted_device_removing_config_entries(hass, registry, update_events):
@ -568,14 +577,19 @@ async def test_deleted_device_removing_config_entries(hass, registry, update_eve
assert len(update_events) == 5 assert len(update_events) == 5
assert update_events[0]["action"] == "create" assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "update" assert update_events[1]["action"] == "update"
assert update_events[1]["device_id"] == entry2.id assert update_events[1]["device_id"] == entry2.id
assert update_events[1]["changes"] == {"config_entries": {"123"}}
assert update_events[2]["action"] == "create" assert update_events[2]["action"] == "create"
assert update_events[2]["device_id"] == entry3.id assert update_events[2]["device_id"] == entry3.id
assert "changes" not in update_events[2]["device_id"]
assert update_events[3]["action"] == "remove" assert update_events[3]["action"] == "remove"
assert update_events[3]["device_id"] == entry.id assert update_events[3]["device_id"] == entry.id
assert "changes" not in update_events[3]
assert update_events[4]["action"] == "remove" assert update_events[4]["action"] == "remove"
assert update_events[4]["device_id"] == entry3.id assert update_events[4]["device_id"] == entry3.id
assert "changes" not in update_events[4]
registry.async_clear_config_entry("123") registry.async_clear_config_entry("123")
assert len(registry.devices) == 0 assert len(registry.devices) == 0
@ -892,7 +906,7 @@ async def test_format_mac(registry):
assert list(invalid_mac_entry.connections)[0][1] == invalid assert list(invalid_mac_entry.connections)[0][1] == invalid
async def test_update(registry): async def test_update(hass, registry, update_events):
"""Verify that we can update some attributes of a device.""" """Verify that we can update some attributes of a device."""
entry = registry.async_get_or_create( entry = registry.async_get_or_create(
config_entry_id="1234", config_entry_id="1234",
@ -940,6 +954,24 @@ async def test_update(registry):
assert registry.async_get(updated_entry.id) is not None assert registry.async_get(updated_entry.id) is not None
await hass.async_block_till_done()
assert len(update_events) == 2
assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "update"
assert update_events[1]["device_id"] == entry.id
assert update_events[1]["changes"] == {
"area_id": None,
"disabled_by": None,
"identifiers": {("bla", "123"), ("hue", "456")},
"manufacturer": None,
"model": None,
"name_by_user": None,
"via_device_id": None,
}
async def test_update_remove_config_entries(hass, registry, update_events): async def test_update_remove_config_entries(hass, registry, update_events):
"""Make sure we do not get duplicate entries.""" """Make sure we do not get duplicate entries."""
@ -989,17 +1021,22 @@ async def test_update_remove_config_entries(hass, registry, update_events):
assert len(update_events) == 5 assert len(update_events) == 5
assert update_events[0]["action"] == "create" assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "update" assert update_events[1]["action"] == "update"
assert update_events[1]["device_id"] == entry2.id assert update_events[1]["device_id"] == entry2.id
assert update_events[1]["changes"] == {"config_entries": {"123"}}
assert update_events[2]["action"] == "create" assert update_events[2]["action"] == "create"
assert update_events[2]["device_id"] == entry3.id assert update_events[2]["device_id"] == entry3.id
assert "changes" not in update_events[2]
assert update_events[3]["action"] == "update" assert update_events[3]["action"] == "update"
assert update_events[3]["device_id"] == entry.id assert update_events[3]["device_id"] == entry.id
assert update_events[3]["changes"] == {"config_entries": {"456", "123"}}
assert update_events[4]["action"] == "remove" assert update_events[4]["action"] == "remove"
assert update_events[4]["device_id"] == entry3.id assert update_events[4]["device_id"] == entry3.id
assert "changes" not in update_events[4]
async def test_update_sw_version(registry): async def test_update_sw_version(hass, registry, update_events):
"""Verify that we can update software version of a device.""" """Verify that we can update software version of a device."""
entry = registry.async_get_or_create( entry = registry.async_get_or_create(
config_entry_id="1234", config_entry_id="1234",
@ -1016,8 +1053,18 @@ async def test_update_sw_version(registry):
assert updated_entry != entry assert updated_entry != entry
assert updated_entry.sw_version == sw_version assert updated_entry.sw_version == sw_version
await hass.async_block_till_done()
async def test_update_hw_version(registry): assert len(update_events) == 2
assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "update"
assert update_events[1]["device_id"] == entry.id
assert update_events[1]["changes"] == {"sw_version": None}
async def test_update_hw_version(hass, registry, update_events):
"""Verify that we can update hardware version of a device.""" """Verify that we can update hardware version of a device."""
entry = registry.async_get_or_create( entry = registry.async_get_or_create(
config_entry_id="1234", config_entry_id="1234",
@ -1034,8 +1081,18 @@ async def test_update_hw_version(registry):
assert updated_entry != entry assert updated_entry != entry
assert updated_entry.hw_version == hw_version assert updated_entry.hw_version == hw_version
await hass.async_block_till_done()
async def test_update_suggested_area(registry, area_registry): assert len(update_events) == 2
assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "update"
assert update_events[1]["device_id"] == entry.id
assert update_events[1]["changes"] == {"hw_version": None}
async def test_update_suggested_area(hass, registry, area_registry, update_events):
"""Verify that we can update the suggested area version of a device.""" """Verify that we can update the suggested area version of a device."""
entry = registry.async_get_or_create( entry = registry.async_get_or_create(
config_entry_id="1234", config_entry_id="1234",
@ -1061,6 +1118,16 @@ async def test_update_suggested_area(registry, area_registry):
assert updated_entry.area_id == pool_area.id assert updated_entry.area_id == pool_area.id
assert len(area_registry.areas) == 1 assert len(area_registry.areas) == 1
await hass.async_block_till_done()
assert len(update_events) == 2
assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "update"
assert update_events[1]["device_id"] == entry.id
assert update_events[1]["changes"] == {"area_id": None, "suggested_area": None}
async def test_cleanup_device_registry(hass, registry): async def test_cleanup_device_registry(hass, registry):
"""Test cleanup works.""" """Test cleanup works."""
@ -1221,12 +1288,16 @@ async def test_restore_device(hass, registry, update_events):
assert len(update_events) == 4 assert len(update_events) == 4
assert update_events[0]["action"] == "create" assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "remove" assert update_events[1]["action"] == "remove"
assert update_events[1]["device_id"] == entry.id assert update_events[1]["device_id"] == entry.id
assert "changes" not in update_events[1]
assert update_events[2]["action"] == "create" assert update_events[2]["action"] == "create"
assert update_events[2]["device_id"] == entry2.id assert update_events[2]["device_id"] == entry2.id
assert "changes" not in update_events[2]
assert update_events[3]["action"] == "create" assert update_events[3]["action"] == "create"
assert update_events[3]["device_id"] == entry3.id assert update_events[3]["device_id"] == entry3.id
assert "changes" not in update_events[3]
async def test_restore_simple_device(hass, registry, update_events): async def test_restore_simple_device(hass, registry, update_events):
@ -1266,12 +1337,16 @@ async def test_restore_simple_device(hass, registry, update_events):
assert len(update_events) == 4 assert len(update_events) == 4
assert update_events[0]["action"] == "create" assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "remove" assert update_events[1]["action"] == "remove"
assert update_events[1]["device_id"] == entry.id assert update_events[1]["device_id"] == entry.id
assert "changes" not in update_events[1]
assert update_events[2]["action"] == "create" assert update_events[2]["action"] == "create"
assert update_events[2]["device_id"] == entry2.id assert update_events[2]["device_id"] == entry2.id
assert "changes" not in update_events[2]
assert update_events[3]["action"] == "create" assert update_events[3]["action"] == "create"
assert update_events[3]["device_id"] == entry3.id assert update_events[3]["device_id"] == entry3.id
assert "changes" not in update_events[3]
async def test_restore_shared_device(hass, registry, update_events): async def test_restore_shared_device(hass, registry, update_events):
@ -1358,18 +1433,31 @@ async def test_restore_shared_device(hass, registry, update_events):
assert len(update_events) == 7 assert len(update_events) == 7
assert update_events[0]["action"] == "create" assert update_events[0]["action"] == "create"
assert update_events[0]["device_id"] == entry.id assert update_events[0]["device_id"] == entry.id
assert "changes" not in update_events[0]
assert update_events[1]["action"] == "update" assert update_events[1]["action"] == "update"
assert update_events[1]["device_id"] == entry.id assert update_events[1]["device_id"] == entry.id
assert update_events[1]["changes"] == {
"config_entries": {"123"},
"identifiers": {("entry_123", "0123")},
}
assert update_events[2]["action"] == "remove" assert update_events[2]["action"] == "remove"
assert update_events[2]["device_id"] == entry.id assert update_events[2]["device_id"] == entry.id
assert "changes" not in update_events[2]
assert update_events[3]["action"] == "create" assert update_events[3]["action"] == "create"
assert update_events[3]["device_id"] == entry.id assert update_events[3]["device_id"] == entry.id
assert "changes" not in update_events[3]
assert update_events[4]["action"] == "remove" assert update_events[4]["action"] == "remove"
assert update_events[4]["device_id"] == entry.id assert update_events[4]["device_id"] == entry.id
assert "changes" not in update_events[4]
assert update_events[5]["action"] == "create" assert update_events[5]["action"] == "create"
assert update_events[5]["device_id"] == entry.id assert update_events[5]["device_id"] == entry.id
assert update_events[1]["action"] == "update" assert "changes" not in update_events[5]
assert update_events[1]["device_id"] == entry.id assert update_events[6]["action"] == "update"
assert update_events[6]["device_id"] == entry.id
assert update_events[6]["changes"] == {
"config_entries": {"234"},
"identifiers": {("entry_234", "2345")},
}
async def test_get_or_create_empty_then_set_default_values(hass, registry): async def test_get_or_create_empty_then_set_default_values(hass, registry):