Tellduslive refactoring (#18780)

* move component to a package

* move TelldusLiveEntry to separate file

* refactor

* move entities from a shared container
* using the dispatch helper instead for communication between component and platforms

* updated covereagerc and codeowners

* suggestions from MartinHjelmare

* don't make update async

* "Strip is good!"
This commit is contained in:
Fredrik Erlandsson 2018-12-04 10:08:40 +01:00 committed by Martin Hjelmare
parent a6511fc0b9
commit d6a4e106a9
10 changed files with 168 additions and 153 deletions

View file

@ -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

View file

@ -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

View file

@ -9,8 +9,9 @@ https://home-assistant.io/components/binary_sensor.tellduslive/
"""
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
)

View file

@ -8,8 +8,9 @@ https://home-assistant.io/components/cover.tellduslive/
"""
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 @@ class TelldusLiveCover(TelldusLiveEntity, CoverDevice):
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()

View file

@ -8,9 +8,10 @@ https://home-assistant.io/components/light.tellduslive/
"""
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):

View file

@ -6,7 +6,8 @@ https://home-assistant.io/components/sensor.tellduslive/
"""
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_DEW_POINT = 'dewp'
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_TYPES = {
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 @@ class TelldusLiveSensor(TelldusLiveEntity):
@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):

View file

@ -9,7 +9,8 @@ https://home-assistant.io/components/switch.tellduslive/
"""
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 @@ class TelldusLiveSwitch(TelldusLiveEntity, ToggleEntity):
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()

View file

@ -4,25 +4,22 @@ Support for Telldus Live.
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 @@ CONFIG_SCHEMA = vol.Schema({
}),
}, extra=vol.ALLOW_EXTRA)
ATTR_LAST_UPDATED = 'time_last_updated'
CONFIG_INSTRUCTIONS = """
To link your TelldusLive account:
@ -146,7 +140,7 @@ def setup(hass, config, session=None):
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 setup(hass, config, session=None):
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 @@ class TelldusLiveClient:
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 @@ class TelldusLiveClient:
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 @@ class TelldusLiveClient:
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

View file

@ -0,0 +1,5 @@
"""Consts used by TelldusLive."""
DOMAIN = 'tellduslive'
SIGNAL_UPDATE_ENTITY = 'tellduslive_update'

View file

@ -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