Merge pull request #1122 from molobrakos/tellduslive
reworked telldus live support
This commit is contained in:
commit
96066e94ab
3 changed files with 212 additions and 142 deletions
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue