Clean up device update, add via-hub (#16659)
* Clean up device update, add via-hub * Test loading/saving data * Lint * Add to Hue" * Lint + tests
This commit is contained in:
parent
849a93e0a6
commit
b8257866f5
13 changed files with 269 additions and 78 deletions
|
@ -40,6 +40,7 @@ def websocket_list_devices(hass, connection, msg):
|
||||||
'name': entry.name,
|
'name': entry.name,
|
||||||
'sw_version': entry.sw_version,
|
'sw_version': entry.sw_version,
|
||||||
'id': entry.id,
|
'id': entry.id,
|
||||||
|
'hub_device_id': entry.hub_device_id,
|
||||||
} for entry in registry.devices.values()]
|
} for entry in registry.devices.values()]
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
@ -127,7 +127,7 @@ async def async_setup_entry(hass, config_entry):
|
||||||
device_registry = await \
|
device_registry = await \
|
||||||
hass.helpers.device_registry.async_get_registry()
|
hass.helpers.device_registry.async_get_registry()
|
||||||
device_registry.async_get_or_create(
|
device_registry.async_get_or_create(
|
||||||
config_entry=config_entry.entry_id,
|
config_entry_id=config_entry.entry_id,
|
||||||
connections={(CONNECTION_NETWORK_MAC, deconz.config.mac)},
|
connections={(CONNECTION_NETWORK_MAC, deconz.config.mac)},
|
||||||
identifiers={(DOMAIN, deconz.config.bridgeid)},
|
identifiers={(DOMAIN, deconz.config.bridgeid)},
|
||||||
manufacturer='Dresden Elektronik', model=deconz.config.modelid,
|
manufacturer='Dresden Elektronik', model=deconz.config.modelid,
|
||||||
|
|
|
@ -140,7 +140,7 @@ async def async_setup_entry(hass, entry):
|
||||||
config = bridge.api.config
|
config = bridge.api.config
|
||||||
device_registry = await dr.async_get_registry(hass)
|
device_registry = await dr.async_get_registry(hass)
|
||||||
device_registry.async_get_or_create(
|
device_registry.async_get_or_create(
|
||||||
config_entry=entry.entry_id,
|
config_entry_id=entry.entry_id,
|
||||||
connections={
|
connections={
|
||||||
(dr.CONNECTION_NETWORK_MAC, config.mac)
|
(dr.CONNECTION_NETWORK_MAC, config.mac)
|
||||||
},
|
},
|
||||||
|
|
|
@ -302,6 +302,7 @@ class HueLight(Light):
|
||||||
'model': self.light.productname or self.light.modelid,
|
'model': self.light.productname or self.light.modelid,
|
||||||
# Not yet exposed as properties in aiohue
|
# Not yet exposed as properties in aiohue
|
||||||
'sw_version': self.light.raw['swversion'],
|
'sw_version': self.light.raw['swversion'],
|
||||||
|
'via_hub': (hue.DOMAIN, self.bridge.api.config.bridgeid),
|
||||||
}
|
}
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
|
|
|
@ -10,6 +10,7 @@ from homeassistant.core import callback
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
_UNDEF = object()
|
||||||
|
|
||||||
DATA_REGISTRY = 'device_registry'
|
DATA_REGISTRY = 'device_registry'
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ class DeviceEntry:
|
||||||
model = attr.ib(type=str)
|
model = attr.ib(type=str)
|
||||||
name = attr.ib(type=str, default=None)
|
name = attr.ib(type=str, default=None)
|
||||||
sw_version = attr.ib(type=str, default=None)
|
sw_version = attr.ib(type=str, default=None)
|
||||||
|
hub_device_id = attr.ib(type=str, default=None)
|
||||||
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
|
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,28 +56,36 @@ class DeviceRegistry:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_get_or_create(self, *, config_entry, connections, identifiers,
|
def async_get_or_create(self, *, config_entry_id, connections, identifiers,
|
||||||
manufacturer, model, name=None, sw_version=None):
|
manufacturer, model, name=None, sw_version=None,
|
||||||
|
via_hub=None):
|
||||||
"""Get device. Create if it doesn't exist."""
|
"""Get device. Create if it doesn't exist."""
|
||||||
if not identifiers and not connections:
|
if not identifiers and not connections:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
device = self.async_get_device(identifiers, connections)
|
device = self.async_get_device(identifiers, connections)
|
||||||
|
|
||||||
|
if via_hub is not None:
|
||||||
|
hub_device = self.async_get_device({via_hub}, set())
|
||||||
|
hub_device_id = hub_device.id if hub_device else None
|
||||||
|
else:
|
||||||
|
hub_device_id = None
|
||||||
|
|
||||||
if device is not None:
|
if device is not None:
|
||||||
if config_entry not in device.config_entries:
|
return self._async_update_device(
|
||||||
device.config_entries.add(config_entry)
|
device.id, config_entry_id=config_entry_id,
|
||||||
self.async_schedule_save()
|
hub_device_id=hub_device_id
|
||||||
return device
|
)
|
||||||
|
|
||||||
device = DeviceEntry(
|
device = DeviceEntry(
|
||||||
config_entries=[config_entry],
|
config_entries={config_entry_id},
|
||||||
connections=connections,
|
connections=connections,
|
||||||
identifiers=identifiers,
|
identifiers=identifiers,
|
||||||
manufacturer=manufacturer,
|
manufacturer=manufacturer,
|
||||||
model=model,
|
model=model,
|
||||||
name=name,
|
name=name,
|
||||||
sw_version=sw_version
|
sw_version=sw_version,
|
||||||
|
hub_device_id=hub_device_id
|
||||||
)
|
)
|
||||||
self.devices[device.id] = device
|
self.devices[device.id] = device
|
||||||
|
|
||||||
|
@ -83,24 +93,64 @@ class DeviceRegistry:
|
||||||
|
|
||||||
return device
|
return device
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_update_device(self, device_id, *, config_entry_id=_UNDEF,
|
||||||
|
remove_config_entry_id=_UNDEF,
|
||||||
|
hub_device_id=_UNDEF):
|
||||||
|
"""Update device attributes."""
|
||||||
|
old = self.devices[device_id]
|
||||||
|
|
||||||
|
changes = {}
|
||||||
|
|
||||||
|
config_entries = old.config_entries
|
||||||
|
|
||||||
|
if (config_entry_id is not _UNDEF and
|
||||||
|
config_entry_id not in old.config_entries):
|
||||||
|
config_entries = old.config_entries | {config_entry_id}
|
||||||
|
|
||||||
|
if (remove_config_entry_id is not _UNDEF and
|
||||||
|
remove_config_entry_id in config_entries):
|
||||||
|
config_entries = set(config_entries)
|
||||||
|
config_entries.remove(remove_config_entry_id)
|
||||||
|
|
||||||
|
if config_entries is not old.config_entries:
|
||||||
|
changes['config_entries'] = config_entries
|
||||||
|
|
||||||
|
if (hub_device_id is not _UNDEF and
|
||||||
|
hub_device_id != old.hub_device_id):
|
||||||
|
changes['hub_device_id'] = hub_device_id
|
||||||
|
|
||||||
|
if not changes:
|
||||||
|
return old
|
||||||
|
|
||||||
|
new = self.devices[device_id] = attr.evolve(old, **changes)
|
||||||
|
self.async_schedule_save()
|
||||||
|
return new
|
||||||
|
|
||||||
async def async_load(self):
|
async def async_load(self):
|
||||||
"""Load the device registry."""
|
"""Load the device registry."""
|
||||||
devices = await self._store.async_load()
|
data = await self._store.async_load()
|
||||||
|
|
||||||
if devices is None:
|
devices = OrderedDict()
|
||||||
self.devices = OrderedDict()
|
|
||||||
return
|
|
||||||
|
|
||||||
self.devices = {device['id']: DeviceEntry(
|
if data is not None:
|
||||||
config_entries=device['config_entries'],
|
for device in data['devices']:
|
||||||
connections={tuple(conn) for conn in device['connections']},
|
devices[device['id']] = DeviceEntry(
|
||||||
identifiers={tuple(iden) for iden in device['identifiers']},
|
config_entries=set(device['config_entries']),
|
||||||
manufacturer=device['manufacturer'],
|
connections={tuple(conn) for conn
|
||||||
model=device['model'],
|
in device['connections']},
|
||||||
name=device['name'],
|
identifiers={tuple(iden) for iden
|
||||||
sw_version=device['sw_version'],
|
in device['identifiers']},
|
||||||
id=device['id'],
|
manufacturer=device['manufacturer'],
|
||||||
) for device in devices['devices']}
|
model=device['model'],
|
||||||
|
name=device['name'],
|
||||||
|
sw_version=device['sw_version'],
|
||||||
|
id=device['id'],
|
||||||
|
# Introduced in 0.79
|
||||||
|
hub_device_id=device.get('hub_device_id'),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.devices = devices
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_schedule_save(self):
|
def async_schedule_save(self):
|
||||||
|
@ -122,18 +172,19 @@ class DeviceRegistry:
|
||||||
'name': entry.name,
|
'name': entry.name,
|
||||||
'sw_version': entry.sw_version,
|
'sw_version': entry.sw_version,
|
||||||
'id': entry.id,
|
'id': entry.id,
|
||||||
|
'hub_device_id': entry.hub_device_id,
|
||||||
} for entry in self.devices.values()
|
} for entry in self.devices.values()
|
||||||
]
|
]
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_clear_config_entry(self, config_entry):
|
def async_clear_config_entry(self, config_entry_id):
|
||||||
"""Clear config entry from registry entries."""
|
"""Clear config entry from registry entries."""
|
||||||
for device in self.devices.values():
|
for dev_id, device in self.devices.items():
|
||||||
if config_entry in device.config_entries:
|
if config_entry_id in device.config_entries:
|
||||||
device.config_entries.remove(config_entry)
|
self._async_update_device(
|
||||||
self.async_schedule_save()
|
dev_id, remove_config_entry_id=config_entry_id)
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
|
|
|
@ -273,16 +273,19 @@ class EntityPlatform:
|
||||||
config_entry_id = None
|
config_entry_id = None
|
||||||
|
|
||||||
device_info = entity.device_info
|
device_info = entity.device_info
|
||||||
|
|
||||||
if config_entry_id is not None and device_info is not None:
|
if config_entry_id is not None and device_info is not None:
|
||||||
device = device_registry.async_get_or_create(
|
device = device_registry.async_get_or_create(
|
||||||
config_entry=config_entry_id,
|
config_entry_id=config_entry_id,
|
||||||
connections=device_info.get('connections', []),
|
connections=device_info.get('connections') or set(),
|
||||||
identifiers=device_info.get('identifiers', []),
|
identifiers=device_info.get('identifiers') or set(),
|
||||||
manufacturer=device_info.get('manufacturer'),
|
manufacturer=device_info.get('manufacturer'),
|
||||||
model=device_info.get('model'),
|
model=device_info.get('model'),
|
||||||
name=device_info.get('name'),
|
name=device_info.get('name'),
|
||||||
sw_version=device_info.get('sw_version'))
|
sw_version=device_info.get('sw_version'),
|
||||||
device_id = device.id
|
via_hub=device_info.get('via_hub'))
|
||||||
|
if device:
|
||||||
|
device_id = device.id
|
||||||
else:
|
else:
|
||||||
device_id = None
|
device_id = None
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ STORAGE_VERSION = 1
|
||||||
STORAGE_KEY = 'core.entity_registry'
|
STORAGE_KEY = 'core.entity_registry'
|
||||||
|
|
||||||
|
|
||||||
@attr.s(slots=True)
|
@attr.s(slots=True, frozen=True)
|
||||||
class RegistryEntry:
|
class RegistryEntry:
|
||||||
"""Entity Registry Entry."""
|
"""Entity Registry Entry."""
|
||||||
|
|
||||||
|
@ -113,14 +113,9 @@ class EntityRegistry:
|
||||||
"""Get entity. Create if it doesn't exist."""
|
"""Get entity. Create if it doesn't exist."""
|
||||||
entity_id = self.async_get_entity_id(domain, platform, unique_id)
|
entity_id = self.async_get_entity_id(domain, platform, unique_id)
|
||||||
if entity_id:
|
if entity_id:
|
||||||
entry = self.entities[entity_id]
|
return self._async_update_entity(
|
||||||
if entry.config_entry_id == config_entry_id:
|
|
||||||
return entry
|
|
||||||
|
|
||||||
self._async_update_entity(
|
|
||||||
entity_id, config_entry_id=config_entry_id,
|
entity_id, config_entry_id=config_entry_id,
|
||||||
device_id=device_id)
|
device_id=device_id)
|
||||||
return self.entities[entity_id]
|
|
||||||
|
|
||||||
entity_id = self.async_generate_entity_id(
|
entity_id = self.async_generate_entity_id(
|
||||||
domain, suggested_object_id or '{}_{}'.format(platform, unique_id))
|
domain, suggested_object_id or '{}_{}'.format(platform, unique_id))
|
||||||
|
@ -253,10 +248,9 @@ class EntityRegistry:
|
||||||
@callback
|
@callback
|
||||||
def async_clear_config_entry(self, config_entry):
|
def async_clear_config_entry(self, config_entry):
|
||||||
"""Clear config entry from registry entries."""
|
"""Clear config entry from registry entries."""
|
||||||
for entry in self.entities.values():
|
for entity_id, entry in self.entities.items():
|
||||||
if config_entry == entry.config_entry_id:
|
if config_entry == entry.config_entry_id:
|
||||||
entry.config_entry_id = None
|
self._async_update_entity(entity_id, config_entry_id=None)
|
||||||
self.async_schedule_save()
|
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
|
|
|
@ -763,6 +763,11 @@ class MockEntity(entity.Entity):
|
||||||
"""Return True if entity is available."""
|
"""Return True if entity is available."""
|
||||||
return self._handle('available')
|
return self._handle('available')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_info(self):
|
||||||
|
"""Info how it links to a device."""
|
||||||
|
return self._handle('device_info')
|
||||||
|
|
||||||
def _handle(self, attr):
|
def _handle(self, attr):
|
||||||
"""Return attribute value."""
|
"""Return attribute value."""
|
||||||
if attr in self._values:
|
if attr in self._values:
|
||||||
|
|
|
@ -21,15 +21,16 @@ def registry(hass):
|
||||||
async def test_list_devices(hass, client, registry):
|
async def test_list_devices(hass, client, registry):
|
||||||
"""Test list entries."""
|
"""Test list entries."""
|
||||||
registry.async_get_or_create(
|
registry.async_get_or_create(
|
||||||
config_entry='1234',
|
config_entry_id='1234',
|
||||||
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
identifiers={('bridgeid', '0123')},
|
identifiers={('bridgeid', '0123')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
registry.async_get_or_create(
|
registry.async_get_or_create(
|
||||||
config_entry='1234',
|
config_entry_id='1234',
|
||||||
connections={},
|
connections={},
|
||||||
identifiers={('bridgeid', '1234')},
|
identifiers={('bridgeid', '1234')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model',
|
||||||
|
via_hub=('bridgeid', '0123'))
|
||||||
|
|
||||||
await client.send_json({
|
await client.send_json({
|
||||||
'id': 5,
|
'id': 5,
|
||||||
|
@ -37,8 +38,7 @@ async def test_list_devices(hass, client, registry):
|
||||||
})
|
})
|
||||||
msg = await client.receive_json()
|
msg = await client.receive_json()
|
||||||
|
|
||||||
for entry in msg['result']:
|
dev1, dev2 = [entry.pop('id') for entry in msg['result']]
|
||||||
entry.pop('id')
|
|
||||||
|
|
||||||
assert msg['result'] == [
|
assert msg['result'] == [
|
||||||
{
|
{
|
||||||
|
@ -47,7 +47,8 @@ async def test_list_devices(hass, client, registry):
|
||||||
'manufacturer': 'manufacturer',
|
'manufacturer': 'manufacturer',
|
||||||
'model': 'model',
|
'model': 'model',
|
||||||
'name': None,
|
'name': None,
|
||||||
'sw_version': None
|
'sw_version': None,
|
||||||
|
'hub_device_id': None,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'config_entries': ['1234'],
|
'config_entries': ['1234'],
|
||||||
|
@ -55,6 +56,7 @@ async def test_list_devices(hass, client, registry):
|
||||||
'manufacturer': 'manufacturer',
|
'manufacturer': 'manufacturer',
|
||||||
'model': 'model',
|
'model': 'model',
|
||||||
'name': None,
|
'name': None,
|
||||||
'sw_version': None
|
'sw_version': None,
|
||||||
|
'hub_device_id': dev1,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -182,7 +182,7 @@ async def test_config_passed_to_config_entry(hass):
|
||||||
|
|
||||||
assert len(mock_registry.mock_calls) == 1
|
assert len(mock_registry.mock_calls) == 1
|
||||||
assert mock_registry.mock_calls[0][2] == {
|
assert mock_registry.mock_calls[0][2] == {
|
||||||
'config_entry': entry.entry_id,
|
'config_entry_id': entry.entry_id,
|
||||||
'connections': {
|
'connections': {
|
||||||
('mac', 'mock-mac')
|
('mac', 'mock-mac')
|
||||||
},
|
},
|
||||||
|
@ -192,7 +192,7 @@ async def test_config_passed_to_config_entry(hass):
|
||||||
'manufacturer': 'Signify',
|
'manufacturer': 'Signify',
|
||||||
'name': 'mock-name',
|
'name': 'mock-name',
|
||||||
'model': 'mock-modelid',
|
'model': 'mock-modelid',
|
||||||
'sw_version': 'mock-swversion'
|
'sw_version': 'mock-swversion',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.helpers import device_registry
|
from homeassistant.helpers import device_registry
|
||||||
from tests.common import mock_device_registry
|
from tests.common import mock_device_registry, flush_store
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -14,41 +14,41 @@ def registry(hass):
|
||||||
async def test_get_or_create_returns_same_entry(registry):
|
async def test_get_or_create_returns_same_entry(registry):
|
||||||
"""Make sure we do not duplicate entries."""
|
"""Make sure we do not duplicate entries."""
|
||||||
entry = registry.async_get_or_create(
|
entry = registry.async_get_or_create(
|
||||||
config_entry='1234',
|
config_entry_id='1234',
|
||||||
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
identifiers={('bridgeid', '0123')},
|
identifiers={('bridgeid', '0123')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
entry2 = registry.async_get_or_create(
|
entry2 = registry.async_get_or_create(
|
||||||
config_entry='1234',
|
config_entry_id='1234',
|
||||||
connections={('ethernet', '11:22:33:44:55:66:77:88')},
|
connections={('ethernet', '11:22:33:44:55:66:77:88')},
|
||||||
identifiers={('bridgeid', '0123')},
|
identifiers={('bridgeid', '0123')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
entry3 = registry.async_get_or_create(
|
entry3 = registry.async_get_or_create(
|
||||||
config_entry='1234',
|
config_entry_id='1234',
|
||||||
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
identifiers={('bridgeid', '1234')},
|
identifiers={('bridgeid', '1234')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
|
|
||||||
assert len(registry.devices) == 1
|
assert len(registry.devices) == 1
|
||||||
assert entry is entry2
|
assert entry.id == entry2.id
|
||||||
assert entry is entry3
|
assert entry.id == entry3.id
|
||||||
assert entry.identifiers == {('bridgeid', '0123')}
|
assert entry.identifiers == {('bridgeid', '0123')}
|
||||||
|
|
||||||
|
|
||||||
async def test_requirement_for_identifier_or_connection(registry):
|
async def test_requirement_for_identifier_or_connection(registry):
|
||||||
"""Make sure we do require some descriptor of device."""
|
"""Make sure we do require some descriptor of device."""
|
||||||
entry = registry.async_get_or_create(
|
entry = registry.async_get_or_create(
|
||||||
config_entry='1234',
|
config_entry_id='1234',
|
||||||
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
identifiers=set(),
|
identifiers=set(),
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
entry2 = registry.async_get_or_create(
|
entry2 = registry.async_get_or_create(
|
||||||
config_entry='1234',
|
config_entry_id='1234',
|
||||||
connections=set(),
|
connections=set(),
|
||||||
identifiers={('bridgeid', '0123')},
|
identifiers={('bridgeid', '0123')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
entry3 = registry.async_get_or_create(
|
entry3 = registry.async_get_or_create(
|
||||||
config_entry='1234',
|
config_entry_id='1234',
|
||||||
connections=set(),
|
connections=set(),
|
||||||
identifiers=set(),
|
identifiers=set(),
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
|
@ -62,25 +62,25 @@ async def test_requirement_for_identifier_or_connection(registry):
|
||||||
async def test_multiple_config_entries(registry):
|
async def test_multiple_config_entries(registry):
|
||||||
"""Make sure we do not get duplicate entries."""
|
"""Make sure we do not get duplicate entries."""
|
||||||
entry = registry.async_get_or_create(
|
entry = registry.async_get_or_create(
|
||||||
config_entry='123',
|
config_entry_id='123',
|
||||||
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
identifiers={('bridgeid', '0123')},
|
identifiers={('bridgeid', '0123')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
entry2 = registry.async_get_or_create(
|
entry2 = registry.async_get_or_create(
|
||||||
config_entry='456',
|
config_entry_id='456',
|
||||||
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
identifiers={('bridgeid', '0123')},
|
identifiers={('bridgeid', '0123')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
entry3 = registry.async_get_or_create(
|
entry3 = registry.async_get_or_create(
|
||||||
config_entry='123',
|
config_entry_id='123',
|
||||||
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
identifiers={('bridgeid', '0123')},
|
identifiers={('bridgeid', '0123')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
|
|
||||||
assert len(registry.devices) == 1
|
assert len(registry.devices) == 1
|
||||||
assert entry is entry2
|
assert entry.id == entry2.id
|
||||||
assert entry is entry3
|
assert entry.id == entry3.id
|
||||||
assert entry.config_entries == {'123', '456'}
|
assert entry2.config_entries == {'123', '456'}
|
||||||
|
|
||||||
|
|
||||||
async def test_loading_from_storage(hass, hass_storage):
|
async def test_loading_from_storage(hass, hass_storage):
|
||||||
|
@ -118,7 +118,7 @@ async def test_loading_from_storage(hass, hass_storage):
|
||||||
registry = await device_registry.async_get_registry(hass)
|
registry = await device_registry.async_get_registry(hass)
|
||||||
|
|
||||||
entry = registry.async_get_or_create(
|
entry = registry.async_get_or_create(
|
||||||
config_entry='1234',
|
config_entry_id='1234',
|
||||||
connections={('Zigbee', '01.23.45.67.89')},
|
connections={('Zigbee', '01.23.45.67.89')},
|
||||||
identifiers={('serial', '12:34:56:78:90:AB:CD:EF')},
|
identifiers={('serial', '12:34:56:78:90:AB:CD:EF')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
|
@ -129,25 +129,106 @@ async def test_loading_from_storage(hass, hass_storage):
|
||||||
async def test_removing_config_entries(registry):
|
async def test_removing_config_entries(registry):
|
||||||
"""Make sure we do not get duplicate entries."""
|
"""Make sure we do not get duplicate entries."""
|
||||||
entry = registry.async_get_or_create(
|
entry = registry.async_get_or_create(
|
||||||
config_entry='123',
|
config_entry_id='123',
|
||||||
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
identifiers={('bridgeid', '0123')},
|
identifiers={('bridgeid', '0123')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
entry2 = registry.async_get_or_create(
|
entry2 = registry.async_get_or_create(
|
||||||
config_entry='456',
|
config_entry_id='456',
|
||||||
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
identifiers={('bridgeid', '0123')},
|
identifiers={('bridgeid', '0123')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
entry3 = registry.async_get_or_create(
|
entry3 = registry.async_get_or_create(
|
||||||
config_entry='123',
|
config_entry_id='123',
|
||||||
connections={('ethernet', '34:56:78:90:AB:CD:EF:12')},
|
connections={('ethernet', '34:56:78:90:AB:CD:EF:12')},
|
||||||
identifiers={('bridgeid', '4567')},
|
identifiers={('bridgeid', '4567')},
|
||||||
manufacturer='manufacturer', model='model')
|
manufacturer='manufacturer', model='model')
|
||||||
|
|
||||||
assert len(registry.devices) == 2
|
assert len(registry.devices) == 2
|
||||||
assert entry is entry2
|
assert entry.id == entry2.id
|
||||||
assert entry is not entry3
|
assert entry.id != entry3.id
|
||||||
assert entry.config_entries == {'123', '456'}
|
assert entry2.config_entries == {'123', '456'}
|
||||||
|
|
||||||
registry.async_clear_config_entry('123')
|
registry.async_clear_config_entry('123')
|
||||||
|
entry = registry.async_get_device({('bridgeid', '0123')}, set())
|
||||||
|
entry3 = registry.async_get_device({('bridgeid', '4567')}, set())
|
||||||
|
|
||||||
assert entry.config_entries == {'456'}
|
assert entry.config_entries == {'456'}
|
||||||
assert entry3.config_entries == set()
|
assert entry3.config_entries == set()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_specifying_hub_device_create(registry):
|
||||||
|
"""Test specifying a hub and updating."""
|
||||||
|
hub = registry.async_get_or_create(
|
||||||
|
config_entry_id='123',
|
||||||
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
|
identifiers={('hue', '0123')},
|
||||||
|
manufacturer='manufacturer', model='hub')
|
||||||
|
|
||||||
|
light = registry.async_get_or_create(
|
||||||
|
config_entry_id='456',
|
||||||
|
connections=set(),
|
||||||
|
identifiers={('hue', '456')},
|
||||||
|
manufacturer='manufacturer', model='light',
|
||||||
|
via_hub=('hue', '0123'))
|
||||||
|
|
||||||
|
assert light.hub_device_id == hub.id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_specifying_hub_device_update(registry):
|
||||||
|
"""Test specifying a hub and updating."""
|
||||||
|
light = registry.async_get_or_create(
|
||||||
|
config_entry_id='456',
|
||||||
|
connections=set(),
|
||||||
|
identifiers={('hue', '456')},
|
||||||
|
manufacturer='manufacturer', model='light',
|
||||||
|
via_hub=('hue', '0123'))
|
||||||
|
|
||||||
|
assert light.hub_device_id is None
|
||||||
|
|
||||||
|
hub = registry.async_get_or_create(
|
||||||
|
config_entry_id='123',
|
||||||
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
|
identifiers={('hue', '0123')},
|
||||||
|
manufacturer='manufacturer', model='hub')
|
||||||
|
|
||||||
|
light = registry.async_get_or_create(
|
||||||
|
config_entry_id='456',
|
||||||
|
connections=set(),
|
||||||
|
identifiers={('hue', '456')},
|
||||||
|
manufacturer='manufacturer', model='light',
|
||||||
|
via_hub=('hue', '0123'))
|
||||||
|
|
||||||
|
assert light.hub_device_id == hub.id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_loading_saving_data(hass, registry):
|
||||||
|
"""Test that we load/save data correctly."""
|
||||||
|
orig_hub = registry.async_get_or_create(
|
||||||
|
config_entry_id='123',
|
||||||
|
connections={('ethernet', '12:34:56:78:90:AB:CD:EF')},
|
||||||
|
identifiers={('hue', '0123')},
|
||||||
|
manufacturer='manufacturer', model='hub')
|
||||||
|
|
||||||
|
orig_light = registry.async_get_or_create(
|
||||||
|
config_entry_id='456',
|
||||||
|
connections=set(),
|
||||||
|
identifiers={('hue', '456')},
|
||||||
|
manufacturer='manufacturer', model='light',
|
||||||
|
via_hub=('hue', '0123'))
|
||||||
|
|
||||||
|
assert len(registry.devices) == 2
|
||||||
|
|
||||||
|
# Now load written data in new registry
|
||||||
|
registry2 = device_registry.DeviceRegistry(hass)
|
||||||
|
await flush_store(registry._store)
|
||||||
|
await registry2.async_load()
|
||||||
|
|
||||||
|
# Ensure same order
|
||||||
|
assert list(registry.devices) == list(registry2.devices)
|
||||||
|
|
||||||
|
new_hub = registry2.async_get_device({('hue', '0123')}, set())
|
||||||
|
new_light = registry2.async_get_device({('hue', '456')}, set())
|
||||||
|
|
||||||
|
assert orig_hub == new_hub
|
||||||
|
assert orig_light == new_light
|
||||||
|
|
|
@ -676,3 +676,55 @@ async def test_entity_registry_updates_invalid_entity_id(hass):
|
||||||
assert hass.states.get('test_domain.world') is not None
|
assert hass.states.get('test_domain.world') is not None
|
||||||
assert hass.states.get('invalid_entity_id') is None
|
assert hass.states.get('invalid_entity_id') is None
|
||||||
assert hass.states.get('diff_domain.world') is None
|
assert hass.states.get('diff_domain.world') is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_device_info_called(hass):
|
||||||
|
"""Test device info is forwarded correctly."""
|
||||||
|
registry = await hass.helpers.device_registry.async_get_registry()
|
||||||
|
hub = registry.async_get_or_create(
|
||||||
|
config_entry_id='123',
|
||||||
|
connections=set(),
|
||||||
|
identifiers={('hue', 'hub-id')},
|
||||||
|
manufacturer='manufacturer', model='hub'
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
|
"""Mock setup entry method."""
|
||||||
|
async_add_entities([
|
||||||
|
# Invalid device info
|
||||||
|
MockEntity(unique_id='abcd', device_info={}),
|
||||||
|
# Valid device info
|
||||||
|
MockEntity(unique_id='qwer', device_info={
|
||||||
|
'identifiers': {('hue', '1234')},
|
||||||
|
'connections': {('mac', 'abcd')},
|
||||||
|
'manufacturer': 'test-manuf',
|
||||||
|
'model': 'test-model',
|
||||||
|
'name': 'test-name',
|
||||||
|
'sw_version': 'test-sw',
|
||||||
|
'via_hub': ('hue', 'hub-id'),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
return True
|
||||||
|
|
||||||
|
platform = MockPlatform(
|
||||||
|
async_setup_entry=async_setup_entry
|
||||||
|
)
|
||||||
|
config_entry = MockConfigEntry(entry_id='super-mock-id')
|
||||||
|
entity_platform = MockEntityPlatform(
|
||||||
|
hass,
|
||||||
|
platform_name=config_entry.domain,
|
||||||
|
platform=platform
|
||||||
|
)
|
||||||
|
|
||||||
|
assert await entity_platform.async_setup_entry(config_entry)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
device = registry.async_get_device({('hue', '1234')}, set())
|
||||||
|
assert device is not None
|
||||||
|
assert device.identifiers == {('hue', '1234')}
|
||||||
|
assert device.connections == {('mac', 'abcd')}
|
||||||
|
assert device.manufacturer == 'test-manuf'
|
||||||
|
assert device.model == 'test-model'
|
||||||
|
assert device.name == 'test-name'
|
||||||
|
assert device.sw_version == 'test-sw'
|
||||||
|
assert device.hub_device_id == hub.id
|
||||||
|
|
|
@ -6,7 +6,7 @@ import pytest
|
||||||
|
|
||||||
from homeassistant.helpers import entity_registry
|
from homeassistant.helpers import entity_registry
|
||||||
|
|
||||||
from tests.common import mock_registry
|
from tests.common import mock_registry, flush_store
|
||||||
|
|
||||||
|
|
||||||
YAML__OPEN_PATH = 'homeassistant.util.yaml.open'
|
YAML__OPEN_PATH = 'homeassistant.util.yaml.open'
|
||||||
|
@ -77,8 +77,7 @@ async def test_loading_saving_data(hass, registry):
|
||||||
|
|
||||||
# Now load written data in new registry
|
# Now load written data in new registry
|
||||||
registry2 = entity_registry.EntityRegistry(hass)
|
registry2 = entity_registry.EntityRegistry(hass)
|
||||||
registry2._store = registry._store
|
await flush_store(registry._store)
|
||||||
|
|
||||||
await registry2.async_load()
|
await registry2.async_load()
|
||||||
|
|
||||||
# Ensure same order
|
# Ensure same order
|
||||||
|
@ -192,6 +191,8 @@ async def test_removing_config_entry_id(registry):
|
||||||
'light', 'hue', '5678', config_entry_id='mock-id-1')
|
'light', 'hue', '5678', config_entry_id='mock-id-1')
|
||||||
assert entry.config_entry_id == 'mock-id-1'
|
assert entry.config_entry_id == 'mock-id-1'
|
||||||
registry.async_clear_config_entry('mock-id-1')
|
registry.async_clear_config_entry('mock-id-1')
|
||||||
|
|
||||||
|
entry = registry.entities[entry.entity_id]
|
||||||
assert entry.config_entry_id is None
|
assert entry.config_entry_id is None
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue