Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add update events to registries #23746

Merged
merged 2 commits into from
May 8, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
config/*
config2/*
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this for developers that are swapping around between 2 configs.


tests/testing_config/deps
tests/testing_config/home-assistant.log
Expand Down
3 changes: 3 additions & 0 deletions homeassistant/components/websocket_api/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
EVENT_THEMES_UPDATED)
from homeassistant.components.persistent_notification import (
EVENT_PERSISTENT_NOTIFICATIONS_UPDATED)
from homeassistant.helpers.area_registry import EVENT_AREA_REGISTRY_UPDATED
from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED
from homeassistant.helpers.entity_registry import EVENT_ENTITY_REGISTRY_UPDATED

# These are events that do not contain any sensitive data
# Except for state_changed, which is handled accordingly.
Expand Down
26 changes: 24 additions & 2 deletions homeassistant/helpers/area_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
_LOGGER = logging.getLogger(__name__)

DATA_REGISTRY = 'area_registry'

EVENT_AREA_REGISTRY_UPDATED = 'area_registry_updated'
STORAGE_KEY = 'core.area_registry'
STORAGE_VERSION = 1
SAVE_DELAY = 10
Expand Down Expand Up @@ -58,7 +58,14 @@ def async_create(self, name: str) -> AreaEntry:
area = AreaEntry()
self.areas[area.id] = area

return self.async_update(area.id, name=name)
created = self._async_update(area.id, name=name)

self.hass.bus.async_fire(EVENT_AREA_REGISTRY_UPDATED, {
'action': 'create',
'area_id': created.id,
})

return created

async def async_delete(self, area_id: str) -> None:
"""Delete area."""
Expand All @@ -68,10 +75,25 @@ async def async_delete(self, area_id: str) -> None:

del self.areas[area_id]

self.hass.bus.async_fire(EVENT_AREA_REGISTRY_UPDATED, {
'action': 'remove',
'area_id': area_id,
})

self.async_schedule_save()

@callback
def async_update(self, area_id: str, name: str) -> AreaEntry:
"""Update name of area."""
updated = self._async_update(area_id, name)
self.hass.bus.async_fire(EVENT_AREA_REGISTRY_UPDATED, {
'action': 'update',
'area_id': area_id,
})
return updated

@callback
def _async_update(self, area_id: str, name: str) -> AreaEntry:
"""Update name of area."""
old = self.areas[area_id]

Expand Down
15 changes: 13 additions & 2 deletions homeassistant/helpers/device_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
_UNDEF = object()

DATA_REGISTRY = 'device_registry'

EVENT_DEVICE_REGISTRY_UPDATED = 'device_registry_updated'
STORAGE_KEY = 'core.device_registry'
STORAGE_VERSION = 1
SAVE_DELAY = 10
Expand All @@ -42,6 +42,8 @@ class DeviceEntry:
area_id = attr.ib(type=str, default=None)
name_by_user = attr.ib(type=str, default=None)
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
# This value is not stored, just used to keep track of events to fire.
is_new = attr.ib(type=bool, default=False)


def format_mac(mac):
Expand Down Expand Up @@ -111,7 +113,7 @@ def async_get_or_create(self, *, config_entry_id, connections=None,
device = self.async_get_device(identifiers, connections)

if device is None:
device = DeviceEntry()
device = DeviceEntry(is_new=True)
self.devices[device.id] = device

if via_hub is not None:
Expand Down Expand Up @@ -201,11 +203,20 @@ def _async_update_device(self, device_id, *, add_config_entry_id=_UNDEF,
name_by_user != old.name_by_user):
changes['name_by_user'] = name_by_user

if old.is_new:
changes['is_new'] = False

if not changes:
return old

new = self.devices[device_id] = attr.evolve(old, **changes)
self.async_schedule_save()

self.hass.bus.async_fire(EVENT_DEVICE_REGISTRY_UPDATED, {
'action': 'create' if 'is_new' in changes else 'update',
'device_id': new.id,
})

return new

async def async_load(self):
Expand Down
16 changes: 16 additions & 0 deletions homeassistant/helpers/entity_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

PATH_REGISTRY = 'entity_registry.yaml'
DATA_REGISTRY = 'entity_registry'
EVENT_ENTITY_REGISTRY_UPDATED = 'entity_registry_updated'
SAVE_DELAY = 10
_LOGGER = logging.getLogger(__name__)
_UNDEF = object()
Expand Down Expand Up @@ -150,12 +151,22 @@ def async_get_or_create(self, domain, platform, unique_id, *,
_LOGGER.info('Registered new %s.%s entity: %s',
domain, platform, entity_id)
self.async_schedule_save()

self.hass.bus.async_fire(EVENT_ENTITY_REGISTRY_UPDATED, {
'action': 'create',
'entity_id': entity_id
})

return entity

@callback
def async_remove(self, entity_id):
"""Remove an entity from registry."""
self.entities.pop(entity_id)
self.hass.bus.async_fire(EVENT_ENTITY_REGISTRY_UPDATED, {
'action': 'remove',
'entity_id': entity_id
})
self.async_schedule_save()

@callback
Expand Down Expand Up @@ -234,6 +245,11 @@ def _async_update_entity(self, entity_id, *, name=_UNDEF,

self.async_schedule_save()

self.hass.bus.async_fire(EVENT_ENTITY_REGISTRY_UPDATED, {
'action': 'update',
'entity_id': entity_id
})

return new

async def async_load(self):
Expand Down
50 changes: 46 additions & 4 deletions tests/helpers/test_area_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import asynctest
import pytest

from homeassistant.core import callback
from homeassistant.helpers import area_registry
from tests.common import mock_area_registry, flush_store

Expand All @@ -14,6 +15,21 @@ def registry(hass):
return mock_area_registry(hass)


@pytest.fixture
def update_events(hass):
"""Capture update events."""
events = []

@callback
def async_capture(event):
events.append(event.data)

hass.bus.async_listen(area_registry.EVENT_AREA_REGISTRY_UPDATED,
async_capture)

return events


async def test_list_areas(registry):
"""Make sure that we can read areas."""
registry.async_create('mock')
Expand All @@ -23,15 +39,22 @@ async def test_list_areas(registry):
assert len(areas) == len(registry.areas)


async def test_create_area(registry):
async def test_create_area(hass, registry, update_events):
"""Make sure that we can create an area."""
area = registry.async_create('mock')

assert area.name == 'mock'
assert len(registry.areas) == 1

await hass.async_block_till_done()

assert len(update_events) == 1
assert update_events[0]['action'] == 'create'
assert update_events[0]['area_id'] == area.id

async def test_create_area_with_name_already_in_use(registry):

async def test_create_area_with_name_already_in_use(hass, registry,
update_events):
"""Make sure that we can't create an area with a name already in use."""
area1 = registry.async_create('mock')

Expand All @@ -40,17 +63,28 @@ async def test_create_area_with_name_already_in_use(registry):
assert area1 != area2
assert e_info == "Name is already in use"

await hass.async_block_till_done()

assert len(registry.areas) == 1
assert len(update_events) == 1


async def test_delete_area(registry):
async def test_delete_area(hass, registry, update_events):
"""Make sure that we can delete an area."""
area = registry.async_create('mock')

await registry.async_delete(area.id)

assert not registry.areas

await hass.async_block_till_done()

assert len(update_events) == 2
assert update_events[0]['action'] == 'create'
assert update_events[0]['area_id'] == area.id
assert update_events[1]['action'] == 'remove'
assert update_events[1]['area_id'] == area.id


async def test_delete_non_existing_area(registry):
"""Make sure that we can't delete an area that doesn't exist."""
Expand All @@ -62,7 +96,7 @@ async def test_delete_non_existing_area(registry):
assert len(registry.areas) == 1


async def test_update_area(registry):
async def test_update_area(hass, registry, update_events):
"""Make sure that we can read areas."""
area = registry.async_create('mock')

Expand All @@ -72,6 +106,14 @@ async def test_update_area(registry):
assert updated_area.name == 'mock1'
assert len(registry.areas) == 1

await hass.async_block_till_done()

assert len(update_events) == 2
assert update_events[0]['action'] == 'create'
assert update_events[0]['area_id'] == area.id
assert update_events[1]['action'] == 'update'
assert update_events[1]['area_id'] == area.id


async def test_update_area_with_same_name(registry):
"""Make sure that we can reapply the same name to the area."""
Expand Down
43 changes: 41 additions & 2 deletions tests/helpers/test_device_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import asynctest
import pytest

from homeassistant.core import callback
from homeassistant.helpers import device_registry
from tests.common import mock_device_registry, flush_store

Expand All @@ -15,7 +16,22 @@ def registry(hass):
return mock_device_registry(hass)


async def test_get_or_create_returns_same_entry(registry):
@pytest.fixture
def update_events(hass):
"""Capture update events."""
events = []

@callback
def async_capture(event):
events.append(event.data)

hass.bus.async_listen(device_registry.EVENT_DEVICE_REGISTRY_UPDATED,
async_capture)

return events


async def test_get_or_create_returns_same_entry(hass, registry, update_events):
"""Make sure we do not duplicate entries."""
entry = registry.async_get_or_create(
config_entry_id='1234',
Expand Down Expand Up @@ -51,6 +67,15 @@ async def test_get_or_create_returns_same_entry(registry):
assert entry3.name == 'name'
assert entry3.sw_version == 'sw-version'

await hass.async_block_till_done()

# Only 2 update events. The third entry did not generate any changes.
assert len(update_events) == 2
assert update_events[0]['action'] == 'create'
assert update_events[0]['device_id'] == entry.id
assert update_events[1]['action'] == 'update'
assert update_events[1]['device_id'] == entry.id


async def test_requirement_for_identifier_or_connection(registry):
"""Make sure we do require some descriptor of device."""
Expand Down Expand Up @@ -155,7 +180,7 @@ async def test_loading_from_storage(hass, hass_storage):
assert isinstance(entry.config_entries, set)


async def test_removing_config_entries(registry):
async def test_removing_config_entries(hass, registry, update_events):
"""Make sure we do not get duplicate entries."""
entry = registry.async_get_or_create(
config_entry_id='123',
Expand Down Expand Up @@ -191,6 +216,20 @@ async def test_removing_config_entries(registry):
assert entry.config_entries == {'456'}
assert entry3.config_entries == set()

await hass.async_block_till_done()

assert len(update_events) == 5
assert update_events[0]['action'] == 'create'
assert update_events[0]['device_id'] == entry.id
assert update_events[1]['action'] == 'update'
assert update_events[1]['device_id'] == entry2.id
assert update_events[2]['action'] == 'create'
assert update_events[2]['device_id'] == entry3.id
assert update_events[3]['action'] == 'update'
assert update_events[3]['device_id'] == entry.id
assert update_events[4]['action'] == 'update'
assert update_events[4]['device_id'] == entry3.id


async def test_removing_area_id(registry):
"""Make sure we can clear area id."""
Expand Down
Loading