Merge pull request #1122 from molobrakos/tellduslive

reworked telldus live support
This commit is contained in:
Paulus Schoutsen 2016-02-04 20:46:23 -08:00
commit 96066e94ab
3 changed files with 212 additions and 142 deletions

View file

@ -46,51 +46,86 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up Tellstick sensors. """
if discovery_info is None:
return
sensors = tellduslive.NETWORK.get_sensors()
devices = []
for component in sensors:
for sensor in component["data"]:
# one component can have more than one sensor
# (e.g. both humidity and temperature)
devices.append(TelldusLiveSensor(component["id"],
component["name"],
sensor["name"]))
add_devices(devices)
add_devices(TelldusLiveSensor(sensor) for sensor in discovery_info)
class TelldusLiveSensor(Entity):
""" Represents a Telldus Live sensor. """
def __init__(self, sensor_id, sensor_name, sensor_type):
self._sensor_id = sensor_id
self._sensor_type = sensor_type
self._state = None
self._name = "{} {}".format(sensor_name or DEVICE_DEFAULT_NAME,
SENSOR_TYPES[sensor_type][0])
self._last_update = None
self._battery_level = None
def __init__(self, sensor_id):
self._id = sensor_id
self.update()
_LOGGER.debug("created sensor %s", self)
def update(self):
""" update sensor values """
tellduslive.NETWORK.update_sensors()
self._sensor = tellduslive.NETWORK.get_sensor(self._id)
@property
def _sensor_name(self):
return self._sensor["name"]
@property
def _sensor_value(self):
return self._sensor["data"]["value"]
@property
def _sensor_type(self):
return self._sensor["data"]["name"]
@property
def _battery_level(self):
sensor_battery_level = self._sensor.get("battery")
return round(sensor_battery_level * 100 / 255) \
if sensor_battery_level else None
@property
def _last_updated(self):
sensor_last_updated = self._sensor.get("lastUpdated")
return str(datetime.fromtimestamp(sensor_last_updated)) \
if sensor_last_updated else None
@property
def _value_as_temperature(self):
return round(float(self._sensor_value), 1)
@property
def _value_as_humidity(self):
return int(round(float(self._sensor_value)))
@property
def name(self):
""" Returns the name of the device. """
return self._name
return "{} {}".format(self._sensor_name or DEVICE_DEFAULT_NAME,
self.quantity_name)
@property
def available(self):
return not self._sensor.get("offline", False)
@property
def state(self):
""" Returns the state of the device. """
return self._state
if self._sensor_type == SENSOR_TYPE_TEMP:
return self._value_as_temperature
elif self._sensor_type == SENSOR_TYPE_HUMIDITY:
return self._value_as_humidity
@property
def state_attributes(self):
attrs = dict()
if self._battery_level is not None:
attrs[ATTR_BATTERY_LEVEL] = self._battery_level
if self._last_update is not None:
attrs[ATTR_LAST_UPDATED] = self._last_update
if self._last_updated is not None:
attrs[ATTR_LAST_UPDATED] = self._last_updated
return attrs
@property
def quantity_name(self):
""" name of quantity """
return SENSOR_TYPES[self._sensor_type][0]
@property
def unit_of_measurement(self):
return SENSOR_TYPES[self._sensor_type][1]
@ -98,18 +133,3 @@ class TelldusLiveSensor(Entity):
@property
def icon(self):
return SENSOR_TYPES[self._sensor_type][2]
def update(self):
values = tellduslive.NETWORK.get_sensor_value(self._sensor_id,
self._sensor_type)
self._state, self._battery_level, self._last_update = values
self._state = float(self._state)
if self._sensor_type == SENSOR_TYPE_TEMP:
self._state = round(self._state, 1)
elif self._sensor_type == SENSOR_TYPE_HUMIDITY:
self._state = int(round(self._state))
self._battery_level = round(self._battery_level * 100 / 255) # percent
self._last_update = str(datetime.fromtimestamp(self._last_update))

View file

@ -10,7 +10,6 @@ https://home-assistant.io/components/switch.tellduslive/
"""
import logging
from homeassistant.const import (STATE_ON, STATE_OFF, STATE_UNKNOWN)
from homeassistant.components import tellduslive
from homeassistant.helpers.entity import ToggleEntity
@ -21,54 +20,45 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
""" Find and return Tellstick switches. """
if discovery_info is None:
return
switches = tellduslive.NETWORK.get_switches()
add_devices([TelldusLiveSwitch(switch["name"],
switch["id"])
for switch in switches if switch["type"] == "device"])
add_devices(TelldusLiveSwitch(switch) for switch in discovery_info)
class TelldusLiveSwitch(ToggleEntity):
""" Represents a Tellstick switch. """
def __init__(self, name, switch_id):
self._name = name
def __init__(self, switch_id):
self._id = switch_id
self._state = STATE_UNKNOWN
self.update()
_LOGGER.debug("created switch %s", self)
def update(self):
tellduslive.NETWORK.update_switches()
self._switch = tellduslive.NETWORK.get_switch(self._id)
@property
def should_poll(self):
""" Tells Home Assistant to poll this entity. """
return False
return True
@property
def name(self):
""" Returns the name of the switch if any. """
return self._name
return self._switch["name"]
def update(self):
from tellive.live import const
state = tellduslive.NETWORK.get_switch_state(self._id)
if state == const.TELLSTICK_TURNON:
self._state = STATE_ON
elif state == const.TELLSTICK_TURNOFF:
self._state = STATE_OFF
else:
self._state = STATE_UNKNOWN
@property
def available(self):
return not self._switch.get("offline", False)
@property
def is_on(self):
""" True if switch is on. """
return self._state == STATE_ON
from tellive.live import const
return self._switch["state"] == const.TELLSTICK_TURNON
def turn_on(self, **kwargs):
""" Turns the switch on. """
if tellduslive.NETWORK.turn_switch_on(self._id):
self._state = STATE_ON
self.update_ha_state()
tellduslive.NETWORK.turn_switch_on(self._id)
def turn_off(self, **kwargs):
""" Turns the switch off. """
if tellduslive.NETWORK.turn_switch_off(self._id):
self._state = STATE_OFF
self.update_ha_state()
tellduslive.NETWORK.turn_switch_off(self._id)

View file

@ -17,141 +17,201 @@ from homeassistant.const import (
EVENT_PLATFORM_DISCOVERED, ATTR_SERVICE, ATTR_DISCOVERED)
DOMAIN = "tellduslive"
DISCOVER_SWITCHES = "tellduslive.switches"
REQUIREMENTS = ['tellive-py==0.5.2']
_LOGGER = logging.getLogger(__name__)
DISCOVER_SENSORS = "tellduslive.sensors"
DISCOVER_SWITCHES = "tellduslive.switches"
DISCOVERY_TYPES = {"sensor": DISCOVER_SENSORS,
"switch": DISCOVER_SWITCHES}
CONF_PUBLIC_KEY = "public_key"
CONF_PRIVATE_KEY = "private_key"
CONF_TOKEN = "token"
CONF_TOKEN_SECRET = "token_secret"
REQUIREMENTS = ['tellive-py==0.5.2']
_LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_SWITCH_UPDATES = timedelta(minutes=1)
MIN_TIME_BETWEEN_SENSOR_UPDATES = timedelta(minutes=5)
NETWORK = None
# Return cached results if last scan was less then this time ago
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=600)
@Throttle(MIN_TIME_BETWEEN_SWITCH_UPDATES)
def request_switches():
""" make request to online service """
_LOGGER.debug("Updating switches from Telldus Live")
switches = NETWORK.request("devices/list")["device"]
# filter out any group of switches
switches = {switch["id"]: switch for switch in switches
if switch["type"] == "device"}
return switches
@Throttle(MIN_TIME_BETWEEN_SENSOR_UPDATES)
def request_sensors():
""" make request to online service """
_LOGGER.debug("Updating sensors from Telldus Live")
units = NETWORK.request("sensors/list")["sensor"]
# one unit can contain many sensors
sensors = {unit["id"]+sensor["name"]: dict(unit, data=sensor)
for unit in units
for sensor in unit["data"]}
return sensors
class TelldusLiveData(object):
""" Gets the latest data and update the states. """
def __init__(self, hass, config):
public_key = config[DOMAIN].get(CONF_PUBLIC_KEY)
private_key = config[DOMAIN].get(CONF_PRIVATE_KEY)
token = config[DOMAIN].get(CONF_TOKEN)
token_secret = config[DOMAIN].get(CONF_TOKEN_SECRET)
from tellive.client import LiveClient
from tellive.live import TelldusLive
self._sensors = []
self._switches = []
self._switches = {}
self._sensors = {}
self._hass = hass
self._config = config
self._client = LiveClient(public_key=public_key,
private_key=private_key,
access_token=token,
access_secret=token_secret)
self._api = TelldusLive(self._client)
def update(self, hass, config):
""" Send discovery event if component not yet discovered. """
self._update_sensors()
self._update_switches()
for component_name, found_devices, discovery_type in \
(('sensor', self._sensors, DISCOVER_SENSORS),
('switch', self._switches, DISCOVER_SWITCHES)):
if len(found_devices):
component = get_component(component_name)
bootstrap.setup_component(hass, component.DOMAIN, config)
hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery_type,
ATTR_DISCOVERED: {}})
def validate_session(self):
""" Make a dummy request to see if the session is valid """
try:
return 'email' in self.request("user/profile")
except RuntimeError:
return False
def _request(self, what, **params):
""" Sends a request to the Tellstick Live API. """
def discover(self):
""" Update states, will trigger discover """
self.update_sensors()
self.update_switches()
def _discover(self, found_devices, component_name):
""" Send discovery event if component not yet discovered """
if not len(found_devices):
return
_LOGGER.info("discovered %d new %s devices",
len(found_devices), component_name)
component = get_component(component_name)
bootstrap.setup_component(self._hass,
component.DOMAIN,
self._config)
discovery_type = DISCOVERY_TYPES[component_name]
self._hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery_type,
ATTR_DISCOVERED: found_devices})
def request(self, what, **params):
""" Sends a request to the tellstick live API """
from tellive.live import const
supported_methods = const.TELLSTICK_TURNON \
| const.TELLSTICK_TURNOFF \
| const.TELLSTICK_TOGGLE
| const.TELLSTICK_TOGGLE \
# Tellstick device methods not yet supported
# | const.TELLSTICK_BELL \
# | const.TELLSTICK_DIM \
# | const.TELLSTICK_LEARN \
# | const.TELLSTICK_EXECUTE \
# | const.TELLSTICK_UP \
# | const.TELLSTICK_DOWN \
# | const.TELLSTICK_STOP
default_params = {'supportedMethods': supported_methods,
"includeValues": 1,
"includeScale": 1}
"includeScale": 1,
"includeIgnored": 0}
params.update(default_params)
# room for improvement: the telllive library doesn't seem to
# re-use sessions, instead it opens a new session for each request
# this needs to be fixed
response = self._client.request(what, params)
_LOGGER.debug("got response %s", response)
return response
def check_request(self, what, **params):
""" Make request, check result if successful. """
response = self._request(what, **params)
return response['status'] == "success"
def update_devices(self,
local_devices,
remote_devices,
component_name):
""" update local device list and discover new devices """
def validate_session(self):
""" Make a dummy request to see if the session is valid. """
if remote_devices is None:
return local_devices
remote_ids = remote_devices.keys()
local_ids = local_devices.keys()
added_devices = list(remote_ids - local_ids)
self._discover(added_devices,
component_name)
removed_devices = list(local_ids - remote_ids)
remote_devices.update({id: dict(local_devices[id], offline=True)
for id in removed_devices})
return remote_devices
def update_sensors(self):
""" update local list of sensors """
try:
response = self._request("user/profile")
return 'email' in response
except RuntimeError:
return False
self._sensors = self.update_devices(self._sensors,
request_sensors(),
"sensor")
except OSError:
_LOGGER.warning("could not update sensors")
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def _update_sensors(self):
""" Get the latest sensor data from Telldus Live. """
_LOGGER.info("Updating sensors from Telldus Live")
self._sensors = self._request("sensors/list")["sensor"]
def update_switches(self):
""" update local list of switches """
try:
self._switches = self.update_devices(self._switches,
request_switches(),
"switch")
except OSError:
_LOGGER.warning("could not update switches")
def _update_switches(self):
""" Get the configured switches from Telldus Live. """
_LOGGER.info("Updating switches from Telldus Live")
self._switches = self._request("devices/list")["device"]
# filter out any group of switches
self._switches = [switch for switch in self._switches
if switch["type"] == "device"]
def _check_request(self, what, **params):
""" Make request, check result if successful """
response = self.request(what, **params)
return response and response.get('status') == 'success'
def get_sensors(self):
""" Get the configured sensors. """
self._update_sensors()
return self._sensors
def get_switch(self, switch_id):
""" return switch representation """
return self._switches[switch_id]
def get_switches(self):
""" Get the configured switches. """
self._update_switches()
return self._switches
def get_sensor_value(self, sensor_id, sensor_name):
""" Get the latest (possibly cached) sensor value. """
self._update_sensors()
for component in self._sensors:
if component["id"] == sensor_id:
for sensor in component["data"]:
if sensor["name"] == sensor_name:
return (sensor["value"],
component["battery"],
component["lastUpdated"])
def get_switch_state(self, switch_id):
""" Returns the state of an switch. """
_LOGGER.info("Updating switch state from Telldus Live")
response = self._request("device/info", id=switch_id)["state"]
return int(response)
def get_sensor(self, sensor_id):
""" return sensor representation """
return self._sensors[sensor_id]
def turn_switch_on(self, switch_id):
""" Turn switch off. """
return self.check_request("device/turnOn", id=switch_id)
if self._check_request("device/turnOn", id=switch_id):
from tellive.live import const
self.get_switch(switch_id)["state"] = const.TELLSTICK_TURNON
def turn_switch_off(self, switch_id):
""" Turn switch on. """
return self.check_request("device/turnOff", id=switch_id)
if self._check_request("device/turnOff", id=switch_id):
from tellive.live import const
self.get_switch(switch_id)["state"] = const.TELLSTICK_TURNOFF
def setup(hass, config):
@ -181,6 +241,6 @@ def setup(hass, config):
"that can be aquired from https://api.telldus.com/keys/index")
return False
NETWORK.update(hass, config)
NETWORK.discover()
return True