From 0b84eefa2ed29cc748a7d9740a0ec0a3b467163c Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Sun, 23 Dec 2018 19:13:49 +0100 Subject: [PATCH] Add hub- and device-info for tellduslive (#19180) * add hub- and device-info * attempt to make I/O outside event loop * add_executer_job * coroutines * async_get_hubs Co-Authored-By: fredrike * asyncio fixes * do device_info IO when device is discovered * it's called async_add_executor_job * nicer unique_id * add comment * it's called `async_add_executor_job` * hub always contains 'name' * await each new device * add comment to why gather is bad --- .../components/sensor/tellduslive.py | 2 +- .../components/tellduslive/__init__.py | 48 ++++++++++++++++--- homeassistant/components/tellduslive/entry.py | 12 +++++ requirements_all.txt | 2 +- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/sensor/tellduslive.py b/homeassistant/components/sensor/tellduslive.py index 1d662cd8e84..f024f62109b 100644 --- a/homeassistant/components/sensor/tellduslive.py +++ b/homeassistant/components/sensor/tellduslive.py @@ -146,4 +146,4 @@ class TelldusLiveSensor(TelldusLiveEntity): @property def unique_id(self) -> str: """Return a unique ID.""" - return "-".join(map(str, self._id)) + return "{}-{}-{}".format(*self._id) diff --git a/homeassistant/components/tellduslive/__init__.py b/homeassistant/components/tellduslive/__init__.py index b17c2cb3c46..472ba3d36f3 100644 --- a/homeassistant/components/tellduslive/__init__.py +++ b/homeassistant/components/tellduslive/__init__.py @@ -6,6 +6,7 @@ https://home-assistant.io/components/tellduslive/ """ import asyncio from datetime import timedelta +from functools import partial import logging import voluptuous as vol @@ -23,7 +24,7 @@ from .const import ( APPLICATION_NAME = 'Home Assistant' -REQUIREMENTS = ['tellduslive==0.10.4'] +REQUIREMENTS = ['tellduslive==0.10.8'] _LOGGER = logging.getLogger(__name__) @@ -52,7 +53,9 @@ async def async_setup_entry(hass, entry): conf = entry.data[KEY_SESSION] if KEY_HOST in conf: - session = Session(**conf) + # Session(**conf) does blocking IO when + # communicating with local devices. + session = await hass.async_add_executor_job(partial(Session, **conf)) else: session = Session( PUBLIC_KEY, @@ -70,8 +73,8 @@ async def async_setup_entry(hass, entry): client = TelldusLiveClient(hass, entry, session) hass.data[DOMAIN] = client - - await client.update() + await async_add_hubs(hass, client, entry.entry_id) + hass.async_create_task(client.update()) interval = timedelta(seconds=entry.data[KEY_SCAN_INTERVAL]) _LOGGER.debug('Update interval %s', interval) @@ -81,6 +84,21 @@ async def async_setup_entry(hass, entry): return True +async def async_add_hubs(hass, client, entry_id): + """Add the hubs associated with the current client to device_registry.""" + dev_reg = await hass.helpers.device_registry.async_get_registry() + for hub in await client.async_get_hubs(): + _LOGGER.debug("Connected hub %s", hub['name']) + dev_reg.async_get_or_create( + config_entry_id=entry_id, + identifiers={(DOMAIN, hub['id'])}, + manufacturer='Telldus', + name=hub['name'], + model=hub['type'], + sw_version=hub['version'], + ) + + async def async_setup(hass, config): """Set up the Telldus Live component.""" if DOMAIN not in config: @@ -116,11 +134,22 @@ class TelldusLiveClient: def __init__(self, hass, config_entry, session): """Initialize the Tellus data object.""" self._known_devices = set() + self._device_infos = {} self._hass = hass self._config_entry = config_entry self._client = session + async def async_get_hubs(self): + """Return hubs registered for the user.""" + clients = await self._hass.async_add_executor_job( + self._client.get_clients) + return clients or [] + + def device_info(self, device_id): + """Return device info.""" + return self._device_infos.get(device_id) + @staticmethod def identify_device(device): """Find out what type of HA component to create.""" @@ -143,6 +172,10 @@ class TelldusLiveClient: """Discover the component.""" device = self._client.device(device_id) component = self.identify_device(device) + self._device_infos.update({ + device_id: + await self._hass.async_add_executor_job(device.info) + }) async with self._hass.data[DATA_CONFIG_ENTRY_LOCK]: if component not in self._hass.data[CONFIG_ENTRY_IS_SETUP]: await self._hass.config_entries.async_forward_entry_setup( @@ -161,13 +194,14 @@ class TelldusLiveClient: async def update(self, *args): """Periodically poll the servers for current state.""" - _LOGGER.debug('Updating') - if not self._client.update(): + if not await self._hass.async_add_executor_job(self._client.update): _LOGGER.warning('Failed request') dev_ids = {dev.device_id for dev in self._client.devices} new_devices = dev_ids - self._known_devices - await asyncio.gather(*[self._discover(d_id) for d_id in new_devices]) + # just await each discover as `gather` use up all HTTPAdapter pools + for d_id in new_devices: + await self._discover(d_id) self._known_devices |= new_devices async_dispatcher_send(self._hass, SIGNAL_UPDATE_ENTITY) diff --git a/homeassistant/components/tellduslive/entry.py b/homeassistant/components/tellduslive/entry.py index 88b7d47ad9d..929dc700afb 100644 --- a/homeassistant/components/tellduslive/entry.py +++ b/homeassistant/components/tellduslive/entry.py @@ -111,3 +111,15 @@ class TelldusLiveEntity(Entity): def unique_id(self) -> str: """Return a unique ID.""" return self._id + + @property + def device_info(self): + """Return device info.""" + device = self._client.device_info(self.device.device_id) + return { + 'identifiers': {('tellduslive', self.device.device_id)}, + 'name': self.device.name, + 'model': device['model'].title(), + 'manufacturer': device['protocol'].title(), + 'via_hub': ('tellduslive', device.get('client')), + } diff --git a/requirements_all.txt b/requirements_all.txt index e272c57401e..7bb0a0490d2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1562,7 +1562,7 @@ tellcore-net==0.4 tellcore-py==1.1.2 # homeassistant.components.tellduslive -tellduslive==0.10.4 +tellduslive==0.10.8 # homeassistant.components.media_player.lg_soundbar temescal==0.1