diff --git a/.coveragerc b/.coveragerc index ecfafa916e4f20..10e07dc2da5b52 100644 --- a/.coveragerc +++ b/.coveragerc @@ -329,7 +329,8 @@ omit = homeassistant/components/tahoma.py homeassistant/components/*/tahoma.py - homeassistant/components/tellduslive.py + homeassistant/components/tellduslive/__init__.py + homeassistant/components/tellduslive/entry.py homeassistant/components/*/tellduslive.py homeassistant/components/tellstick.py diff --git a/CODEOWNERS b/CODEOWNERS index 1bb62d154fdc10..659f434d14b271 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -236,8 +236,8 @@ homeassistant/components/*/simplisafe.py @bachya # T homeassistant/components/tahoma.py @philklei homeassistant/components/*/tahoma.py @philklei -homeassistant/components/tellduslive.py @molobrakos @fredrike -homeassistant/components/*/tellduslive.py @molobrakos @fredrike +homeassistant/components/tellduslive/*.py @fredrike +homeassistant/components/*/tellduslive.py @fredrike homeassistant/components/tesla.py @zabuldon homeassistant/components/*/tesla.py @zabuldon homeassistant/components/thethingsnetwork.py @fabaff diff --git a/homeassistant/components/binary_sensor/tellduslive.py b/homeassistant/components/binary_sensor/tellduslive.py index 450a5e580bdc09..7f60e40c68bb43 100644 --- a/homeassistant/components/binary_sensor/tellduslive.py +++ b/homeassistant/components/binary_sensor/tellduslive.py @@ -9,8 +9,9 @@ """ import logging -from homeassistant.components.tellduslive import TelldusLiveEntity +from homeassistant.components import tellduslive from homeassistant.components.binary_sensor import BinarySensorDevice +from homeassistant.components.tellduslive.entry import TelldusLiveEntity _LOGGER = logging.getLogger(__name__) @@ -19,8 +20,9 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tellstick sensors.""" if discovery_info is None: return + client = hass.data[tellduslive.DOMAIN] add_entities( - TelldusLiveSensor(hass, binary_sensor) + TelldusLiveSensor(client, binary_sensor) for binary_sensor in discovery_info ) diff --git a/homeassistant/components/cover/tellduslive.py b/homeassistant/components/cover/tellduslive.py index 9d292d9e8b5bb3..67affdae04e4b9 100644 --- a/homeassistant/components/cover/tellduslive.py +++ b/homeassistant/components/cover/tellduslive.py @@ -8,8 +8,9 @@ """ import logging +from homeassistant.components import tellduslive from homeassistant.components.cover import CoverDevice -from homeassistant.components.tellduslive import TelldusLiveEntity +from homeassistant.components.tellduslive.entry import TelldusLiveEntity _LOGGER = logging.getLogger(__name__) @@ -19,7 +20,8 @@ def setup_platform(hass, config, add_entities, discovery_info=None): if discovery_info is None: return - add_entities(TelldusLiveCover(hass, cover) for cover in discovery_info) + client = hass.data[tellduslive.DOMAIN] + add_entities(TelldusLiveCover(client, cover) for cover in discovery_info) class TelldusLiveCover(TelldusLiveEntity, CoverDevice): @@ -33,14 +35,11 @@ def is_closed(self): def close_cover(self, **kwargs): """Close the cover.""" self.device.down() - self.changed() def open_cover(self, **kwargs): """Open the cover.""" self.device.up() - self.changed() def stop_cover(self, **kwargs): """Stop the cover.""" self.device.stop() - self.changed() diff --git a/homeassistant/components/light/tellduslive.py b/homeassistant/components/light/tellduslive.py index 07b5458fa4506c..8601fe3cf1f581 100644 --- a/homeassistant/components/light/tellduslive.py +++ b/homeassistant/components/light/tellduslive.py @@ -8,9 +8,10 @@ """ import logging +from homeassistant.components import tellduslive from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light) -from homeassistant.components.tellduslive import TelldusLiveEntity +from homeassistant.components.tellduslive.entry import TelldusLiveEntity _LOGGER = logging.getLogger(__name__) @@ -19,21 +20,21 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tellstick Net lights.""" if discovery_info is None: return - add_entities(TelldusLiveLight(hass, light) for light in discovery_info) + client = hass.data[tellduslive.DOMAIN] + add_entities(TelldusLiveLight(client, light) for light in discovery_info) class TelldusLiveLight(TelldusLiveEntity, Light): """Representation of a Tellstick Net light.""" - def __init__(self, hass, device_id): + def __init__(self, client, device_id): """Initialize the Tellstick Net light.""" - super().__init__(hass, device_id) + super().__init__(client, device_id) self._last_brightness = self.brightness def changed(self): """Define a property of the device that might have changed.""" self._last_brightness = self.brightness - super().changed() @property def brightness(self): diff --git a/homeassistant/components/sensor/tellduslive.py b/homeassistant/components/sensor/tellduslive.py index 9bd5a1d8413192..4afff115b9df4a 100644 --- a/homeassistant/components/sensor/tellduslive.py +++ b/homeassistant/components/sensor/tellduslive.py @@ -6,7 +6,8 @@ """ import logging -from homeassistant.components.tellduslive import TelldusLiveEntity +from homeassistant.components import tellduslive +from homeassistant.components.tellduslive.entry import TelldusLiveEntity from homeassistant.const import ( DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS) @@ -27,8 +28,8 @@ SENSOR_TYPE_BAROMETRIC_PRESSURE = 'barpress' SENSOR_TYPES = { - SENSOR_TYPE_TEMPERATURE: ['Temperature', TEMP_CELSIUS, None, - DEVICE_CLASS_TEMPERATURE], + SENSOR_TYPE_TEMPERATURE: + ['Temperature', TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE], SENSOR_TYPE_HUMIDITY: ['Humidity', '%', None, DEVICE_CLASS_HUMIDITY], SENSOR_TYPE_RAINRATE: ['Rain rate', 'mm/h', 'mdi:water', None], SENSOR_TYPE_RAINTOTAL: ['Rain total', 'mm', 'mdi:water', None], @@ -39,7 +40,7 @@ SENSOR_TYPE_WATT: ['Power', 'W', '', None], SENSOR_TYPE_LUMINANCE: ['Luminance', 'lx', None, DEVICE_CLASS_ILLUMINANCE], SENSOR_TYPE_DEW_POINT: - ['Dew Point', TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE], + ['Dew Point', TEMP_CELSIUS, None, DEVICE_CLASS_TEMPERATURE], SENSOR_TYPE_BAROMETRIC_PRESSURE: ['Barometric Pressure', 'kPa', '', None], } @@ -48,7 +49,9 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Tellstick sensors.""" if discovery_info is None: return - add_entities(TelldusLiveSensor(hass, sensor) for sensor in discovery_info) + client = hass.data[tellduslive.DOMAIN] + add_entities( + TelldusLiveSensor(client, sensor) for sensor in discovery_info) class TelldusLiveSensor(TelldusLiveEntity): @@ -87,9 +90,7 @@ def _value_as_humidity(self): @property def name(self): """Return the name of the sensor.""" - return '{} {}'.format( - super().name, - self.quantity_name or '') + return '{} {}'.format(super().name, self.quantity_name or '').strip() @property def state(self): diff --git a/homeassistant/components/switch/tellduslive.py b/homeassistant/components/switch/tellduslive.py index 0263dfd8198c97..ed4f825f5ac6b3 100644 --- a/homeassistant/components/switch/tellduslive.py +++ b/homeassistant/components/switch/tellduslive.py @@ -9,7 +9,8 @@ """ import logging -from homeassistant.components.tellduslive import TelldusLiveEntity +from homeassistant.components import tellduslive +from homeassistant.components.tellduslive.entry import TelldusLiveEntity from homeassistant.helpers.entity import ToggleEntity _LOGGER = logging.getLogger(__name__) @@ -19,7 +20,9 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up Tellstick switches.""" if discovery_info is None: return - add_entities(TelldusLiveSwitch(hass, switch) for switch in discovery_info) + client = hass.data[tellduslive.DOMAIN] + add_entities( + TelldusLiveSwitch(client, switch) for switch in discovery_info) class TelldusLiveSwitch(TelldusLiveEntity, ToggleEntity): @@ -33,9 +36,7 @@ def is_on(self): def turn_on(self, **kwargs): """Turn the switch on.""" self.device.turn_on() - self.changed() def turn_off(self, **kwargs): """Turn the switch off.""" self.device.turn_off() - self.changed() diff --git a/homeassistant/components/tellduslive.py b/homeassistant/components/tellduslive/__init__.py similarity index 68% rename from homeassistant/components/tellduslive.py rename to homeassistant/components/tellduslive/__init__.py index a6ba248b99b4e8..89e7446448999e 100644 --- a/homeassistant/components/tellduslive.py +++ b/homeassistant/components/tellduslive/__init__.py @@ -4,25 +4,22 @@ For more details about this component, please refer to the documentation at https://home-assistant.io/components/tellduslive/ """ -from datetime import datetime, timedelta +from datetime import timedelta import logging import voluptuous as vol from homeassistant.components.discovery import SERVICE_TELLDUSLIVE -from homeassistant.const import ( - ATTR_BATTERY_LEVEL, CONF_HOST, CONF_TOKEN, DEVICE_DEFAULT_NAME, - EVENT_HOMEASSISTANT_START) +from homeassistant.const import CONF_HOST, CONF_TOKEN from homeassistant.helpers import discovery import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.entity import Entity -from homeassistant.helpers.event import track_point_in_utc_time -from homeassistant.util.dt import utcnow +from homeassistant.helpers.dispatcher import dispatcher_send +from homeassistant.helpers.event import track_time_interval from homeassistant.util.json import load_json, save_json -APPLICATION_NAME = 'Home Assistant' +from .const import DOMAIN, SIGNAL_UPDATE_ENTITY -DOMAIN = 'tellduslive' +APPLICATION_NAME = 'Home Assistant' REQUIREMENTS = ['tellduslive==0.10.4'] @@ -48,9 +45,6 @@ }), }, extra=vol.ALLOW_EXTRA) - -ATTR_LAST_UPDATED = 'time_last_updated' - CONFIG_INSTRUCTIONS = """ To link your TelldusLive account: @@ -146,7 +140,7 @@ def tellstick_discovered(service, info): if not supports_local_api(device): _LOGGER.debug('Tellstick does not support local API') # Configure the cloud service - hass.async_add_job(request_configuration) + hass.add_job(request_configuration) return _LOGGER.debug('Tellstick does support local API') @@ -189,18 +183,17 @@ def tellstick_discovered(service, info): return True if not session.is_authorized: - _LOGGER.error( - 'Authentication Error') + _LOGGER.error('Authentication Error') return False client = TelldusLiveClient(hass, config, session) - hass.data[DOMAIN] = client + client.update() - if session: - client.update() - else: - hass.bus.listen(EVENT_HOMEASSISTANT_START, client.update) + interval = config.get(DOMAIN, {}).get(CONF_UPDATE_INTERVAL, + DEFAULT_UPDATE_INTERVAL) + _LOGGER.debug('Update interval %s', interval) + track_time_interval(hass, client.update, interval) return True @@ -210,27 +203,15 @@ class TelldusLiveClient: def __init__(self, hass, config, session): """Initialize the Tellus data object.""" - self.entities = [] + self._known_devices = set() self._hass = hass self._config = config - - self._interval = config.get(DOMAIN, {}).get( - CONF_UPDATE_INTERVAL, DEFAULT_UPDATE_INTERVAL) - _LOGGER.debug('Update interval %s', self._interval) self._client = session def update(self, *args): - """Periodically poll the servers for current state.""" - _LOGGER.debug('Updating') - try: - self._sync() - finally: - track_point_in_utc_time( - self._hass, self.update, utcnow() + self._interval) - - def _sync(self): """Update local list of devices.""" + _LOGGER.debug('Updating') if not self._client.update(): _LOGGER.warning('Failed request') @@ -254,9 +235,8 @@ def discover(device_id, component): discovery.load_platform( self._hass, component, DOMAIN, [device_id], self._config) - known_ids = {entity.device_id for entity in self.entities} for device in self._client.devices: - if device.device_id in known_ids: + if device.device_id in self._known_devices: continue if device.is_sensor: for item in device.items: @@ -265,9 +245,9 @@ def discover(device_id, component): else: discover(device.device_id, identify_device(device)) + self._known_devices.add(device.device_id) - for entity in self.entities: - entity.changed() + dispatcher_send(self._hass, SIGNAL_UPDATE_ENTITY) def device(self, device_id): """Return device representation.""" @@ -276,91 +256,3 @@ def device(self, device_id): def is_available(self, device_id): """Return device availability.""" return device_id in self._client.device_ids - - -class TelldusLiveEntity(Entity): - """Base class for all Telldus Live entities.""" - - def __init__(self, hass, device_id): - """Initialize the entity.""" - self._id = device_id - self._client = hass.data[DOMAIN] - self._client.entities.append(self) - self._name = self.device.name - _LOGGER.debug('Created device %s', self) - - def changed(self): - """Return the property of the device might have changed.""" - if self.device.name: - self._name = self.device.name - self.schedule_update_ha_state() - - @property - def device_id(self): - """Return the id of the device.""" - return self._id - - @property - def device(self): - """Return the representation of the device.""" - return self._client.device(self.device_id) - - @property - def _state(self): - """Return the state of the device.""" - return self.device.state - - @property - def should_poll(self): - """Return the polling state.""" - return False - - @property - def assumed_state(self): - """Return true if unable to access real state of entity.""" - return True - - @property - def name(self): - """Return name of device.""" - return self._name or DEVICE_DEFAULT_NAME - - @property - def available(self): - """Return true if device is not offline.""" - return self._client.is_available(self.device_id) - - @property - def device_state_attributes(self): - """Return the state attributes.""" - attrs = {} - if self._battery_level: - attrs[ATTR_BATTERY_LEVEL] = self._battery_level - if self._last_updated: - attrs[ATTR_LAST_UPDATED] = self._last_updated - return attrs - - @property - def _battery_level(self): - """Return the battery level of a device.""" - from tellduslive import (BATTERY_LOW, - BATTERY_UNKNOWN, - BATTERY_OK) - if self.device.battery == BATTERY_LOW: - return 1 - if self.device.battery == BATTERY_UNKNOWN: - return None - if self.device.battery == BATTERY_OK: - return 100 - return self.device.battery # Percentage - - @property - def _last_updated(self): - """Return the last update of a device.""" - return str(datetime.fromtimestamp(self.device.lastUpdated)) \ - if self.device.lastUpdated else None - - @property - def unique_id(self) -> str: - """Return a unique ID.""" - return self._id diff --git a/homeassistant/components/tellduslive/const.py b/homeassistant/components/tellduslive/const.py new file mode 100644 index 00000000000000..a4ef33af5186d6 --- /dev/null +++ b/homeassistant/components/tellduslive/const.py @@ -0,0 +1,5 @@ +"""Consts used by TelldusLive.""" + +DOMAIN = 'tellduslive' + +SIGNAL_UPDATE_ENTITY = 'tellduslive_update' diff --git a/homeassistant/components/tellduslive/entry.py b/homeassistant/components/tellduslive/entry.py new file mode 100644 index 00000000000000..88b7d47ad9dc27 --- /dev/null +++ b/homeassistant/components/tellduslive/entry.py @@ -0,0 +1,113 @@ +"""Base Entity for all TelldusLiveEntities.""" +from datetime import datetime +import logging + +from homeassistant.const import ATTR_BATTERY_LEVEL, DEVICE_DEFAULT_NAME +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import Entity + +from .const import SIGNAL_UPDATE_ENTITY + +_LOGGER = logging.getLogger(__name__) + +ATTR_LAST_UPDATED = 'time_last_updated' + + +class TelldusLiveEntity(Entity): + """Base class for all Telldus Live entities.""" + + def __init__(self, client, device_id): + """Initialize the entity.""" + self._id = device_id + self._client = client + self._name = self.device.name + self._async_unsub_dispatcher_connect = None + + async def async_added_to_hass(self): + """Call when entity is added to hass.""" + _LOGGER.debug('Created device %s', self) + self._async_unsub_dispatcher_connect = async_dispatcher_connect( + self.hass, SIGNAL_UPDATE_ENTITY, self._update_callback) + + async def async_will_remove_from_hass(self): + """Disconnect dispatcher listener when removed.""" + if self._async_unsub_dispatcher_connect: + self._async_unsub_dispatcher_connect() + + @callback + def _update_callback(self): + """Return the property of the device might have changed.""" + if self.device.name: + self._name = self.device.name + self.async_schedule_update_ha_state() + + @property + def device_id(self): + """Return the id of the device.""" + return self._id + + @property + def device(self): + """Return the representation of the device.""" + return self._client.device(self.device_id) + + @property + def _state(self): + """Return the state of the device.""" + return self.device.state + + @property + def should_poll(self): + """Return the polling state.""" + return False + + @property + def assumed_state(self): + """Return true if unable to access real state of entity.""" + return True + + @property + def name(self): + """Return name of device.""" + return self._name or DEVICE_DEFAULT_NAME + + @property + def available(self): + """Return true if device is not offline.""" + return self._client.is_available(self.device_id) + + @property + def device_state_attributes(self): + """Return the state attributes.""" + attrs = {} + if self._battery_level: + attrs[ATTR_BATTERY_LEVEL] = self._battery_level + if self._last_updated: + attrs[ATTR_LAST_UPDATED] = self._last_updated + return attrs + + @property + def _battery_level(self): + """Return the battery level of a device.""" + from tellduslive import (BATTERY_LOW, + BATTERY_UNKNOWN, + BATTERY_OK) + if self.device.battery == BATTERY_LOW: + return 1 + if self.device.battery == BATTERY_UNKNOWN: + return None + if self.device.battery == BATTERY_OK: + return 100 + return self.device.battery # Percentage + + @property + def _last_updated(self): + """Return the last update of a device.""" + return str(datetime.fromtimestamp(self.device.lastUpdated)) \ + if self.device.lastUpdated else None + + @property + def unique_id(self) -> str: + """Return a unique ID.""" + return self._id