-
-
Notifications
You must be signed in to change notification settings - Fork 31.3k
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
Device Registry #15980
Device Registry #15980
Conversation
registry = await async_get_registry(hass) | ||
|
||
tasks = [ | ||
self._async_add_entity(entity, update_before_add, | ||
component_entities, registry) | ||
component_entities, registry, device_registry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
line too long (81 > 79 characters)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's rename registry -> entity_registry so we keep our sanity :)
@@ -209,11 +210,13 @@ def add_entities(self, new_entities, update_before_add=False): | |||
hass = self.hass | |||
component_entities = set(hass.states.async_entity_ids(self.domain)) | |||
|
|||
|
|||
device_registry = async_get_device_registry(self.hass) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
too many blank lines (2)
How can I get all entities of one device? I feel that a Demo device included demo light, demo switch, demo sensor will better than use deconz as demo. And also easy to write unit test for demo device. |
class DeviceEntry: | ||
"""Device Registry Entry.""" | ||
|
||
unique_id = attr.ib(type=str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's name this id
and use a factory to automatically generate it
id = attr.ib(type=str, default=attr.Factory(lambda: uuid.uuid4().hex))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind, read up on unique_id. Drop this comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So after reading everything, let's circle back here:
We should add the id
property to DeviceEntry
, it will be how we can point at this device from the entity registry.
For unique_id and serial, I would expect us to have a tuple of identifiers. Like connection, but with just identifying things:
identifiers = (
('serial', 'abcdefgh'), # if we can read the device serial
('nest', 'czxcz'), # if the developer wants to pass an identifier. They can use their domain to scope it.
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then we would only do identification on identifiers, or connections are still viable due to discovery?
serial = attr.ib(type=str) | ||
manufacturer = attr.ib(type=str) | ||
model = attr.ib(type=str) | ||
connection = attr.ib(type=tuple) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the idea was for this to be a tuple of tuples. A device can have multiple connections. For example a Raspberry Pi has WiFi and LAN connection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we use some kind of standardisation for the types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CONNECTION_ZIGBEE = 'zigbee'
CONNECTION_MAC = 'mac'
I guess we can also have duplicates (wifi + lan = 2 macs ?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder how often you use both wifi and lan for a device that is connected to hass
model = attr.ib(type=str) | ||
connection = attr.ib(type=tuple) | ||
sw_version = attr.ib(type=str, default=None) | ||
config_entry_id = attr.ib(type=set, default=attr.Factory(set)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize now that this is not the right place.
Instead, this will be linked via the Entity Registry which will contain a reference to the device registry and to the config entry.
serial = self._sensor.uniqueid.split('-', 1)[0] | ||
else: | ||
return None | ||
dev = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want this to be a loosely defined dictionary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As opposed to using get_create_device in each entity.device? I started in this way, not set in stone
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could return an instance of a class for this purpose, similar to the DeviceEntry
class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could use async_get_create_device directly, then entity_platform would only do the coupling between entity_registry and device_registry.
def async_get_device(self, serial: str, connection: tuple): | ||
"""Check if device is registered.""" | ||
for device in self.devices.values(): | ||
if (device.serial == serial or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now we're treating serial as unique ID?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following the discussion up top originally; first it was unique id, it was then moved to serial. Regardless, gonna redo it all to your suggested identifier tuple
@@ -209,11 +210,13 @@ def add_entities(self, new_entities, update_before_add=False): | |||
hass = self.hass | |||
component_entities = set(hass.states.async_entity_ids(self.domain)) | |||
|
|||
|
|||
device_registry = async_get_device_registry(self.hass) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of this, you could, albeit more lines, type:
device_registry = hass.helpers.device_registry.async_get_registry()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I do the same for entity registry?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah
@@ -269,10 +272,22 @@ def add_entities(self, new_entities, update_before_add=False): | |||
else: | |||
config_entry_id = None | |||
|
|||
if entity.device is not None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
entity.device
will actually execute code so better to just request it once instead of 8 times.
device = entity.device
if device is not None:
entity.device['connection'], | ||
sw_version=entity.device.get('sw_version'), | ||
config_entry_id=config_entry_id) | ||
device_id = device.unique_id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be device.id
, we need to generate our own IDs for devices and not trust developers.
This is off to a great start ! Left some comments. Other suggestions:
|
Thanks for the feedback! Everything but tests I should be able to get fixed tonight. |
@balloob if we do persistent storage, should I cover removal of entities -> devices with the initial implementation? |
device_registry = (await \ | ||
hass.helpers.device_registry.async_get_registry()) | ||
entity_registry = (await \ | ||
hass.helpers.entity_registry.async_get_registry()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
continuation line under-indented for visual indent
registry = await async_get_registry(hass) | ||
device_registry = (await \ | ||
hass.helpers.device_registry.async_get_registry()) | ||
entity_registry = (await \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the backslash is redundant between brackets
@@ -209,11 +208,15 @@ def add_entities(self, new_entities, update_before_add=False): | |||
hass = self.hass | |||
component_entities = set(hass.states.async_entity_ids(self.domain)) | |||
|
|||
registry = await async_get_registry(hass) | |||
device_registry = (await \ | |||
hass.helpers.device_registry.async_get_registry()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
continuation line under-indented for visual indent
@@ -209,11 +208,15 @@ def add_entities(self, new_entities, update_before_add=False): | |||
hass = self.hass | |||
component_entities = set(hass.states.async_entity_ids(self.domain)) | |||
|
|||
registry = await async_get_registry(hass) | |||
device_registry = (await \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the backslash is redundant between brackets
return None | ||
|
||
async def async_get_or_create(self, identifiers, manufacturer, model, | ||
connection, *, sw_version=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
continuation line under-indented for visual indent
@@ -12,6 +12,8 @@ | |||
CONF_ID, CONF_PORT, EVENT_HOMEASSISTANT_STOP) | |||
from homeassistant.core import EventOrigin, callback | |||
from homeassistant.helpers import aiohttp_client, config_validation as cv | |||
from homeassistant.helpers.device_registry import ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'homeassistant.helpers.device_registry.async_get_registry as async_get_device_registry' imported but unused
Let's not cover removal, we do that in a future PR. |
For inspiration on how to handle storage, see #16018 |
19a6e30
to
59f5787
Compare
@@ -210,6 +218,7 @@ def _async_update_entity(self, entity_id, *, name=_UNDEF, | |||
entities[entity['entity_id']] = RegistryEntry( | |||
entity_id=entity['entity_id'], | |||
config_entry_id=entity.get('config_entry_id'), | |||
device_id=info.get('device_id'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undefined name 'info'
35d0f8e
to
552e5c2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool!
if any(identifier in device.identifiers | ||
for identifier in identifiers) or \ | ||
any(connection in device.connection | ||
for connection in connections): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
continuation line over-indented for visual indent
🎉 |
Now we need to update the developer docs, so we can show developers how to use this. |
Description:
Device registry based on home-assistant/architecture#36
Related issue (if applicable): fixes #
Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.github.io#<home-assistant.github.io PR number goes here>
Checklist:
tox
. Your PR cannot be merged unless tests passIf user exposed functionality or configuration variables are added/changed:
If the code does not interact with devices: