Allow overriding name via entity registry (#12292)
* Allow overriding name via entity registry * Update requirements
This commit is contained in:
parent
8b9eab196c
commit
17e5740a0c
11 changed files with 91 additions and 21 deletions
|
@ -226,7 +226,6 @@ class XiaomiMiioRemote(RemoteDevice):
|
|||
_LOGGER.error("Device does not support turn_off, " +
|
||||
"please use 'remote.send_command' to send commands.")
|
||||
|
||||
# pylint: enable=R0201
|
||||
def _send_command(self, payload):
|
||||
"""Send a command."""
|
||||
from miio import DeviceException
|
||||
|
|
|
@ -80,6 +80,9 @@ class Entity(object):
|
|||
# Process updates in parallel
|
||||
parallel_updates = None
|
||||
|
||||
# Name in the entity registry
|
||||
registry_name = None
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Return True if entity has to be polled for state.
|
||||
|
@ -225,7 +228,7 @@ class Entity(object):
|
|||
if unit_of_measurement is not None:
|
||||
attr[ATTR_UNIT_OF_MEASUREMENT] = unit_of_measurement
|
||||
|
||||
name = self.name
|
||||
name = self.registry_name or self.name
|
||||
if name is not None:
|
||||
attr[ATTR_FRIENDLY_NAME] = name
|
||||
|
||||
|
|
|
@ -40,19 +40,19 @@ class EntityComponent(object):
|
|||
self.config = None
|
||||
|
||||
self._platforms = {
|
||||
'core': EntityPlatform(
|
||||
domain: EntityPlatform(
|
||||
hass=hass,
|
||||
logger=logger,
|
||||
domain=domain,
|
||||
platform_name='core',
|
||||
platform_name=domain,
|
||||
scan_interval=self.scan_interval,
|
||||
parallel_updates=0,
|
||||
entity_namespace=None,
|
||||
async_entities_added_callback=self._async_update_group,
|
||||
)
|
||||
}
|
||||
self.async_add_entities = self._platforms['core'].async_add_entities
|
||||
self.add_entities = self._platforms['core'].add_entities
|
||||
self.async_add_entities = self._platforms[domain].async_add_entities
|
||||
self.add_entities = self._platforms[domain].add_entities
|
||||
|
||||
@property
|
||||
def entities(self):
|
||||
|
@ -190,7 +190,7 @@ class EntityComponent(object):
|
|||
yield from asyncio.wait(tasks, loop=self.hass.loop)
|
||||
|
||||
self._platforms = {
|
||||
'core': self._platforms['core']
|
||||
self.domain: self._platforms[self.domain]
|
||||
}
|
||||
self.config = None
|
||||
|
||||
|
|
|
@ -213,6 +213,7 @@ class EntityPlatform(object):
|
|||
self.domain, self.platform_name, entity.unique_id,
|
||||
suggested_object_id=suggested_object_id)
|
||||
entity.entity_id = entry.entity_id
|
||||
entity.registry_name = entry.name
|
||||
|
||||
# We won't generate an entity ID if the platform has already set one
|
||||
# We will however make sure that platform cannot pick a registered ID
|
||||
|
|
|
@ -11,22 +11,37 @@ After initializing, call EntityRegistry.async_ensure_loaded to load the data
|
|||
from disk.
|
||||
"""
|
||||
import asyncio
|
||||
from collections import namedtuple, OrderedDict
|
||||
from collections import OrderedDict
|
||||
from itertools import chain
|
||||
import logging
|
||||
import os
|
||||
|
||||
import attr
|
||||
|
||||
from ..core import callback, split_entity_id
|
||||
from ..util import ensure_unique_string, slugify
|
||||
from ..util.yaml import load_yaml, save_yaml
|
||||
|
||||
PATH_REGISTRY = 'entity_registry.yaml'
|
||||
SAVE_DELAY = 10
|
||||
Entry = namedtuple('EntityRegistryEntry',
|
||||
'entity_id,unique_id,platform,domain')
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@attr.s(slots=True, frozen=True)
|
||||
class RegistryEntry:
|
||||
"""Entity Registry Entry."""
|
||||
|
||||
entity_id = attr.ib(type=str)
|
||||
unique_id = attr.ib(type=str)
|
||||
platform = attr.ib(type=str)
|
||||
name = attr.ib(type=str, default=None)
|
||||
domain = attr.ib(type=str, default=None, init=False, repr=False)
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
"""Computed properties."""
|
||||
object.__setattr__(self, "domain", split_entity_id(self.entity_id)[0])
|
||||
|
||||
|
||||
class EntityRegistry:
|
||||
"""Class to hold a registry of entities."""
|
||||
|
||||
|
@ -65,11 +80,10 @@ class EntityRegistry:
|
|||
|
||||
entity_id = self.async_generate_entity_id(
|
||||
domain, suggested_object_id or '{}_{}'.format(platform, unique_id))
|
||||
entity = Entry(
|
||||
entity = RegistryEntry(
|
||||
entity_id=entity_id,
|
||||
unique_id=unique_id,
|
||||
platform=platform,
|
||||
domain=domain,
|
||||
)
|
||||
self.entities[entity_id] = entity
|
||||
_LOGGER.info('Registered new %s.%s entity: %s',
|
||||
|
@ -98,11 +112,11 @@ class EntityRegistry:
|
|||
data = yield from self.hass.async_add_job(load_yaml, path)
|
||||
|
||||
for entity_id, info in data.items():
|
||||
entities[entity_id] = Entry(
|
||||
domain=split_entity_id(entity_id)[0],
|
||||
entities[entity_id] = RegistryEntry(
|
||||
entity_id=entity_id,
|
||||
unique_id=info['unique_id'],
|
||||
platform=info['platform']
|
||||
platform=info['platform'],
|
||||
name=info.get('name')
|
||||
)
|
||||
|
||||
self.entities = entities
|
||||
|
|
|
@ -11,6 +11,7 @@ async_timeout==2.0.0
|
|||
chardet==3.0.4
|
||||
astral==1.5
|
||||
certifi>=2017.4.17
|
||||
attrs==17.4.0
|
||||
|
||||
# Breaks Python 3.6 and is not needed for our supported Pythons
|
||||
enum34==1000000000.0.0
|
||||
|
|
|
@ -12,6 +12,7 @@ async_timeout==2.0.0
|
|||
chardet==3.0.4
|
||||
astral==1.5
|
||||
certifi>=2017.4.17
|
||||
attrs==17.4.0
|
||||
|
||||
# homeassistant.components.nuimo_controller
|
||||
--only-binary=all https://github.com/getSenic/nuimo-linux-python/archive/29fc42987f74d8090d0e2382e8f248ff5990b8c9.zip#nuimo==1.0.0
|
||||
|
|
1
setup.py
1
setup.py
|
@ -61,6 +61,7 @@ REQUIRES = [
|
|||
'chardet==3.0.4',
|
||||
'astral==1.5',
|
||||
'certifi>=2017.4.17',
|
||||
'attrs==17.4.0',
|
||||
]
|
||||
|
||||
MIN_PY_VERSION = '.'.join(map(
|
||||
|
|
|
@ -317,10 +317,10 @@ def mock_component(hass, component):
|
|||
hass.config.components.add(component)
|
||||
|
||||
|
||||
def mock_registry(hass):
|
||||
def mock_registry(hass, mock_entries=None):
|
||||
"""Mock the Entity Registry."""
|
||||
registry = entity_registry.EntityRegistry(hass)
|
||||
registry.entities = {}
|
||||
registry.entities = mock_entries or {}
|
||||
hass.data[entity_platform.DATA_REGISTRY] = registry
|
||||
return registry
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import homeassistant.loader as loader
|
|||
from homeassistant.helpers.entity import generate_entity_id
|
||||
from homeassistant.helpers.entity_component import (
|
||||
EntityComponent, DEFAULT_SCAN_INTERVAL)
|
||||
from homeassistant.helpers import entity_platform
|
||||
from homeassistant.helpers import entity_platform, entity_registry
|
||||
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
|
@ -433,3 +433,24 @@ def test_entity_with_name_and_entity_id_getting_registered(hass):
|
|||
MockEntity(unique_id='1234', name='bla',
|
||||
entity_id='test_domain.world')])
|
||||
assert 'test_domain.world' in hass.states.async_entity_ids()
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_overriding_name_from_registry(hass):
|
||||
"""Test that we can override a name via the Entity Registry."""
|
||||
component = EntityComponent(_LOGGER, DOMAIN, hass)
|
||||
mock_registry(hass, {
|
||||
'test_domain.world': entity_registry.RegistryEntry(
|
||||
entity_id='test_domain.world',
|
||||
unique_id='1234',
|
||||
# Using component.async_add_entities is equal to platform "domain"
|
||||
platform='test_domain',
|
||||
name='Overridden'
|
||||
)
|
||||
})
|
||||
yield from component.async_add_entities([
|
||||
MockEntity(unique_id='1234', name='Device Name')])
|
||||
|
||||
state = hass.states.get('test_domain.world')
|
||||
assert state is not None
|
||||
assert state.name == 'Overridden'
|
||||
|
|
|
@ -9,6 +9,9 @@ from homeassistant.helpers import entity_registry
|
|||
from tests.common import mock_registry
|
||||
|
||||
|
||||
YAML__OPEN_PATH = 'homeassistant.util.yaml.open'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def registry(hass):
|
||||
"""Return an empty, loaded, registry."""
|
||||
|
@ -82,13 +85,12 @@ def test_save_timer_reset_on_subsequent_save(hass, registry):
|
|||
@asyncio.coroutine
|
||||
def test_loading_saving_data(hass, registry):
|
||||
"""Test that we load/save data correctly."""
|
||||
yaml_path = 'homeassistant.util.yaml.open'
|
||||
orig_entry1 = registry.async_get_or_create('light', 'hue', '1234')
|
||||
orig_entry2 = registry.async_get_or_create('light', 'hue', '5678')
|
||||
|
||||
assert len(registry.entities) == 2
|
||||
|
||||
with patch(yaml_path, mock_open(), create=True) as mock_write:
|
||||
with patch(YAML__OPEN_PATH, mock_open(), create=True) as mock_write:
|
||||
yield from registry._async_save()
|
||||
|
||||
# Mock open calls are: open file, context enter, write, context leave
|
||||
|
@ -98,7 +100,7 @@ def test_loading_saving_data(hass, registry):
|
|||
registry2 = entity_registry.EntityRegistry(hass)
|
||||
|
||||
with patch('os.path.isfile', return_value=True), \
|
||||
patch(yaml_path, mock_open(read_data=written), create=True):
|
||||
patch(YAML__OPEN_PATH, mock_open(read_data=written), create=True):
|
||||
yield from registry2._async_load()
|
||||
|
||||
# Ensure same order
|
||||
|
@ -133,3 +135,30 @@ def test_is_registered(registry):
|
|||
entry = registry.async_get_or_create('light', 'hue', '1234')
|
||||
assert registry.async_is_registered(entry.entity_id)
|
||||
assert not registry.async_is_registered('light.non_existing')
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_loading_extra_values(hass):
|
||||
"""Test we load extra data from the registry."""
|
||||
written = """
|
||||
test.named:
|
||||
platform: super_platform
|
||||
unique_id: with-name
|
||||
name: registry override
|
||||
test.no_name:
|
||||
platform: super_platform
|
||||
unique_id: without-name
|
||||
"""
|
||||
|
||||
registry = entity_registry.EntityRegistry(hass)
|
||||
|
||||
with patch('os.path.isfile', return_value=True), \
|
||||
patch(YAML__OPEN_PATH, mock_open(read_data=written), create=True):
|
||||
yield from registry._async_load()
|
||||
|
||||
entry_with_name = registry.async_get_or_create(
|
||||
'test', 'super_platform', 'with-name')
|
||||
entry_without_name = registry.async_get_or_create(
|
||||
'test', 'super_platform', 'without-name')
|
||||
assert entry_with_name.name == 'registry override'
|
||||
assert entry_without_name.name is None
|
||||
|
|
Loading…
Add table
Reference in a new issue