From 6015274ee2486f797fd6ee8f5f2074a601953e03 Mon Sep 17 00:00:00 2001 From: thecynic Date: Wed, 25 Jan 2017 00:11:37 -0800 Subject: [PATCH] Add initial support for Lutron RadioRA 2 using pylutron (#5337) Signed-off-by: Dima Zavin --- .coveragerc | 3 + homeassistant/components/light/lutron.py | 98 ++++++++++++++++++++++++ homeassistant/components/lutron.py | 86 +++++++++++++++++++++ requirements_all.txt | 3 + 4 files changed, 190 insertions(+) create mode 100644 homeassistant/components/light/lutron.py create mode 100644 homeassistant/components/lutron.py diff --git a/.coveragerc b/.coveragerc index c1b47123710..b5dda5b6f43 100644 --- a/.coveragerc +++ b/.coveragerc @@ -46,6 +46,9 @@ omit = homeassistant/components/isy994.py homeassistant/components/*/isy994.py + homeassistant/components/lutron.py + homeassistant/components/*/lutron.py + homeassistant/components/modbus.py homeassistant/components/*/modbus.py diff --git a/homeassistant/components/light/lutron.py b/homeassistant/components/light/lutron.py new file mode 100644 index 00000000000..7bc6fb50571 --- /dev/null +++ b/homeassistant/components/light/lutron.py @@ -0,0 +1,98 @@ +"""Support for Lutron lights.""" +import logging + +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, DOMAIN, SUPPORT_BRIGHTNESS, Light) +from homeassistant.components.lutron import ( + LutronDevice, LUTRON_DEVICES, LUTRON_GROUPS, LUTRON_CONTROLLER) + +DEPENDENCIES = ['lutron'] + +_LOGGER = logging.getLogger(__name__) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup Lutron lights.""" + area_devs = {} + devs = [] + for (area_name, device) in hass.data[LUTRON_DEVICES]['light']: + dev = LutronLight(hass, area_name, device, + hass.data[LUTRON_CONTROLLER]) + area_devs.setdefault(area_name, []).append(dev) + devs.append(dev) + add_devices(devs, True) + + for area in area_devs: + if area not in hass.data[LUTRON_GROUPS]: + continue + grp = hass.data[LUTRON_GROUPS][area] + ids = list(grp.tracking) + [dev.entity_id for dev in area_devs[area]] + grp.update_tracked_entity_ids(ids) + + return True + + +def to_lutron_level(level): + """Convert the given HASS light level (0-255) to Lutron (0.0-100.0).""" + return float((level * 100) / 255) + + +def to_hass_level(level): + """Convert the given Lutron (0.0-100.0) light level to HASS (0-255).""" + return int((level * 255) / 100) + + +class LutronLight(LutronDevice, Light): + """Representation of a Lutron Light, including dimmable.""" + + def __init__(self, hass, area_name, lutron_device, controller): + """Initialize the light.""" + self._prev_brightness = None + LutronDevice.__init__(self, hass, DOMAIN, area_name, lutron_device, + controller) + + @property + def supported_features(self): + """Flag supported features.""" + return SUPPORT_BRIGHTNESS + + @property + def brightness(self): + """Return the brightness of the light.""" + new_brightness = to_hass_level(self._lutron_device.last_level()) + if new_brightness != 0: + self._prev_brightness = new_brightness + return new_brightness + + def turn_on(self, **kwargs): + """Turn the light on.""" + if ATTR_BRIGHTNESS in kwargs and self._lutron_device.is_dimmable: + brightness = kwargs[ATTR_BRIGHTNESS] + elif self._prev_brightness == 0: + brightness = 255 / 2 + else: + brightness = self._prev_brightness + self._prev_brightness = brightness + self._lutron_device.level = to_lutron_level(brightness) + + def turn_off(self, **kwargs): + """Turn the light off.""" + self._lutron_device.level = 0 + + @property + def device_state_attributes(self): + """Return the state attributes.""" + attr = {} + attr['Lutron Integration ID'] = self._lutron_device.id + return attr + + @property + def is_on(self): + """Return true if device is on.""" + return self._lutron_device.last_level() > 0 + + def update(self): + """Called when forcing a refresh of the device.""" + if self._prev_brightness is None: + self._prev_brightness = to_hass_level(self._lutron_device.level) diff --git a/homeassistant/components/lutron.py b/homeassistant/components/lutron.py new file mode 100644 index 00000000000..0c9458ff3f6 --- /dev/null +++ b/homeassistant/components/lutron.py @@ -0,0 +1,86 @@ +""" +Component for interacting with a Lutron RadioRA 2 system. + +Uses pylutron (http://github.com/thecynic/pylutron). +""" + +import logging + +from homeassistant.helpers import discovery +from homeassistant.helpers.entity import (Entity, generate_entity_id) +from homeassistant.loader import get_component + +REQUIREMENTS = ['https://github.com/thecynic/pylutron/archive/v0.1.0.zip#' + 'pylutron==0.1.0'] + +DOMAIN = "lutron" + +_LOGGER = logging.getLogger(__name__) + +LUTRON_CONTROLLER = 'lutron_controller' +LUTRON_DEVICES = 'lutron_devices' +LUTRON_GROUPS = 'lutron_groups' + + +def setup(hass, base_config): + """Setup the Lutron component.""" + from pylutron import Lutron + + hass.data[LUTRON_CONTROLLER] = None + hass.data[LUTRON_DEVICES] = {'light': []} + hass.data[LUTRON_GROUPS] = {} + + config = base_config.get(DOMAIN) + hass.data[LUTRON_CONTROLLER] = Lutron( + config['lutron_host'], + config['lutron_user'], + config['lutron_password'] + ) + hass.data[LUTRON_CONTROLLER].load_xml_db() + hass.data[LUTRON_CONTROLLER].connect() + _LOGGER.info("Connected to Main Repeater @ %s", config['lutron_host']) + + group = get_component('group') + + # Sort our devices into types + for area in hass.data[LUTRON_CONTROLLER].areas: + if area.name not in hass.data[LUTRON_GROUPS]: + grp = group.Group.create_group(hass, area.name, []) + hass.data[LUTRON_GROUPS][area.name] = grp + for output in area.outputs: + hass.data[LUTRON_DEVICES]['light'].append((area.name, output)) + + for component in ('light',): + discovery.load_platform(hass, component, DOMAIN, None, base_config) + return True + + +class LutronDevice(Entity): + """Representation of a Lutron device entity.""" + + def __init__(self, hass, domain, area_name, lutron_device, controller): + """Initialize the device.""" + self._lutron_device = lutron_device + self._controller = controller + self._area_name = area_name + + self.hass = hass + object_id = '{} {}'.format(area_name, lutron_device.name) + self.entity_id = generate_entity_id(domain + '.{}', object_id, + hass=hass) + + self._controller.subscribe(self._lutron_device, self._update_callback) + + def _update_callback(self, _device): + """Callback invoked by pylutron when the device state changes.""" + self.schedule_update_ha_state() + + @property + def name(self): + """Return the name of the device.""" + return self._lutron_device.name + + @property + def should_poll(self): + """No polling needed.""" + return False diff --git a/requirements_all.txt b/requirements_all.txt index 6bb84a897de..b086258b9fd 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -260,6 +260,9 @@ https://github.com/soldag/pyflic/archive/0.4.zip#pyflic==0.4 # homeassistant.components.light.osramlightify https://github.com/tfriedel/python-lightify/archive/d6eadcf311e6e21746182d1480e97b350dda2b3e.zip#lightify==1.0.4 +# homeassistant.components.lutron +https://github.com/thecynic/pylutron/archive/v0.1.0.zip#pylutron==0.1.0 + # homeassistant.components.mysensors https://github.com/theolind/pymysensors/archive/0b705119389be58332f17753c53167f551254b6c.zip#pymysensors==0.8