core/homeassistant/components/tellduslive/__init__.py

219 lines
7.0 KiB
Python
Raw Normal View History

"""Support for Telldus Live."""
import asyncio
from functools import partial
import logging
2016-09-11 07:22:49 +00:00
from tellduslive import DIM, TURNON, UP, Session
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_SCAN_INTERVAL
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_call_later
from .const import (
2019-07-31 19:25:30 +00:00
DOMAIN,
KEY_SCAN_INTERVAL,
KEY_SESSION,
MIN_UPDATE_INTERVAL,
NOT_SO_PRIVATE_KEY,
PUBLIC_KEY,
SCAN_INTERVAL,
SIGNAL_UPDATE_ENTITY,
TELLDUS_DISCOVERY_NEW,
)
2019-07-31 19:25:30 +00:00
APPLICATION_NAME = "Home Assistant"
2016-02-03 21:31:28 +00:00
_LOGGER = logging.getLogger(__name__)
2019-07-31 19:25:30 +00:00
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(CONF_HOST, default=DOMAIN): cv.string,
vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): vol.All(
cv.time_period, vol.Clamp(min=MIN_UPDATE_INTERVAL)
),
}
)
},
extra=vol.ALLOW_EXTRA,
)
2019-07-31 19:25:30 +00:00
DATA_CONFIG_ENTRY_LOCK = "tellduslive_config_entry_lock"
CONFIG_ENTRY_IS_SETUP = "telldus_config_entry_is_setup"
2019-07-31 19:25:30 +00:00
NEW_CLIENT_TASK = "telldus_new_client_task"
INTERVAL_TRACKER = f"{DOMAIN}_INTERVAL"
async def async_setup_entry(hass, entry):
"""Create a tellduslive session."""
conf = entry.data[KEY_SESSION]
2016-09-11 07:22:49 +00:00
if CONF_HOST in conf:
# Session(**conf) does blocking IO when
# communicating with local devices.
session = await hass.async_add_executor_job(partial(Session, **conf))
else:
session = Session(
2019-07-31 19:25:30 +00:00
PUBLIC_KEY, NOT_SO_PRIVATE_KEY, application=APPLICATION_NAME, **conf
)
if not session.is_authorized:
2019-07-31 19:25:30 +00:00
_LOGGER.error("Authentication Error")
return False
hass.data[DATA_CONFIG_ENTRY_LOCK] = asyncio.Lock()
hass.data[CONFIG_ENTRY_IS_SETUP] = set()
hass.data[NEW_CLIENT_TASK] = hass.loop.create_task(
2019-07-31 19:25:30 +00:00
async_new_client(hass, session, entry)
)
return True
2016-09-11 07:22:49 +00:00
async def async_new_client(hass, session, entry):
"""Add the hubs associated with the current client to device_registry."""
interval = entry.data[KEY_SCAN_INTERVAL]
_LOGGER.debug("Update interval %s seconds", interval)
client = TelldusLiveClient(hass, entry, session, interval)
hass.data[DOMAIN] = client
dev_reg = await hass.helpers.device_registry.async_get_registry()
for hub in await client.async_get_hubs():
2019-07-31 19:25:30 +00:00
_LOGGER.debug("Connected hub %s", hub["name"])
dev_reg.async_get_or_create(
config_entry_id=entry.entry_id,
2019-07-31 19:25:30 +00:00
identifiers={(DOMAIN, hub["id"])},
manufacturer="Telldus",
name=hub["name"],
model=hub["type"],
sw_version=hub["version"],
)
await client.update()
async def async_setup(hass, config):
"""Set up the Telldus Live component."""
if DOMAIN not in config:
return True
2016-09-11 07:22:49 +00:00
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
2019-07-31 19:25:30 +00:00
context={"source": config_entries.SOURCE_IMPORT},
data={
CONF_HOST: config[DOMAIN].get(CONF_HOST),
KEY_SCAN_INTERVAL: config[DOMAIN][CONF_SCAN_INTERVAL],
2019-07-31 19:25:30 +00:00
},
)
)
return True
2016-09-11 07:22:49 +00:00
async def async_unload_entry(hass, config_entry):
"""Unload a config entry."""
if not hass.data[NEW_CLIENT_TASK].done():
hass.data[NEW_CLIENT_TASK].cancel()
interval_tracker = hass.data.pop(INTERVAL_TRACKER)
interval_tracker()
unload_ok = await hass.config_entries.async_unload_platforms(
config_entry, CONFIG_ENTRY_IS_SETUP
2019-07-31 19:25:30 +00:00
)
del hass.data[DOMAIN]
del hass.data[DATA_CONFIG_ENTRY_LOCK]
del hass.data[CONFIG_ENTRY_IS_SETUP]
return unload_ok
2016-09-11 07:22:49 +00:00
2016-02-03 21:31:28 +00:00
class TelldusLiveClient:
2016-03-08 16:55:57 +00:00
"""Get the latest data and update the states."""
def __init__(self, hass, config_entry, session, interval):
2016-03-08 16:55:57 +00:00
"""Initialize the Tellus data object."""
self._known_devices = set()
self._device_infos = {}
2016-02-03 21:31:28 +00:00
self._hass = hass
self._config_entry = config_entry
self._client = session
self._interval = interval
2016-02-03 21:31:28 +00:00
async def async_get_hubs(self):
"""Return hubs registered for the user."""
2019-07-31 19:25:30 +00:00
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."""
if device.is_sensor:
2019-07-31 19:25:30 +00:00
return "sensor"
if device.methods & DIM:
2019-07-31 19:25:30 +00:00
return "light"
if device.methods & UP:
2019-07-31 19:25:30 +00:00
return "cover"
if device.methods & TURNON:
2019-07-31 19:25:30 +00:00
return "switch"
if device.methods == 0:
2019-07-31 19:25:30 +00:00
return "binary_sensor"
_LOGGER.warning("Unidentified device type (methods: %d)", device.methods)
return "switch"
async def _discover(self, device_id):
"""Discover the component."""
device = self._client.device(device_id)
component = self.identify_device(device)
2019-07-31 19:25:30 +00:00
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(
2019-07-31 19:25:30 +00:00
self._config_entry, component
)
self._hass.data[CONFIG_ENTRY_IS_SETUP].add(component)
device_ids = []
if device.is_sensor:
for item in device.items:
device_ids.append((device.device_id, item.name, item.scale))
else:
device_ids.append(device_id)
for _id in device_ids:
async_dispatcher_send(
2019-07-31 19:25:30 +00:00
self._hass, TELLDUS_DISCOVERY_NEW.format(component, DOMAIN), _id
)
async def update(self, *args):
"""Periodically poll the servers for current state."""
try:
2019-07-31 19:25:30 +00:00
if not await self._hass.async_add_executor_job(self._client.update):
_LOGGER.warning("Failed request")
return
dev_ids = {dev.device_id for dev in self._client.devices}
new_devices = dev_ids - self._known_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)
finally:
self._hass.data[INTERVAL_TRACKER] = async_call_later(
2019-07-31 19:25:30 +00:00
self._hass, self._interval, self.update
)
def device(self, device_id):
"""Return device representation."""
2017-01-31 19:08:11 +00:00
return self._client.device(device_id)
def is_available(self, device_id):
"""Return device availability."""
return device_id in self._client.device_ids