Merge pull request #671 from happyleavesaoc/limitlessled

limitlessled improvements
This commit is contained in:
Paulus Schoutsen 2015-11-29 16:26:22 -08:00
commit 6a25af4a9f
2 changed files with 229 additions and 124 deletions

View File

@ -8,171 +8,276 @@ https://home-assistant.io/components/light.limitlessled/
""" """
import logging import logging
from homeassistant.const import DEVICE_DEFAULT_NAME
from homeassistant.components.light import (Light, ATTR_BRIGHTNESS, from homeassistant.components.light import (Light, ATTR_BRIGHTNESS,
ATTR_RGB_COLOR, ATTR_EFFECT, ATTR_RGB_COLOR, ATTR_EFFECT,
ATTR_COLOR_TEMP, ATTR_TRANSITION,
ATTR_FLASH, FLASH_LONG,
EFFECT_COLORLOOP, EFFECT_WHITE) EFFECT_COLORLOOP, EFFECT_WHITE)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['ledcontroller==1.1.0'] REQUIREMENTS = ['limitlessled==1.0.0']
RGB_BOUNDARY = 40
COLOR_TABLE = { DEFAULT_TRANSITION = 0
'white': [0xFF, 0xFF, 0xFF], DEFAULT_PORT = 8899
'violet': [0xEE, 0x82, 0xEE], DEFAULT_VERSION = 5
'royal_blue': [0x41, 0x69, 0xE1], DEFAULT_LED_TYPE = 'rgbw'
'baby_blue': [0x87, 0xCE, 0xFA], WHITE = [255, 255, 255]
'aqua': [0x00, 0xFF, 0xFF],
'royal_mint': [0x7F, 0xFF, 0xD4],
'seafoam_green': [0x2E, 0x8B, 0x57],
'green': [0x00, 0x80, 0x00],
'lime_green': [0x32, 0xCD, 0x32],
'yellow': [0xFF, 0xFF, 0x00],
'yellow_orange': [0xDA, 0xA5, 0x20],
'orange': [0xFF, 0xA5, 0x00],
'red': [0xFF, 0x00, 0x00],
'pink': [0xFF, 0xC0, 0xCB],
'fusia': [0xFF, 0x00, 0xFF],
'lilac': [0xDA, 0x70, 0xD6],
'lavendar': [0xE6, 0xE6, 0xFA],
}
def _distance_squared(rgb1, rgb2): def rewrite_legacy(config):
""" Return sum of squared distances of each color part. """ """ Rewrite legacy configuration to new format. """
return sum((val1-val2)**2 for val1, val2 in zip(rgb1, rgb2)) bridges = config.get('bridges', [config])
new_bridges = []
for bridge_conf in bridges:
def _rgb_to_led_color(rgb_color): groups = []
""" Convert an RGB color to the closest color string and color. """ if 'groups' in bridge_conf:
return sorted((_distance_squared(rgb_color, color), name) groups = bridge_conf['groups']
for name, color in COLOR_TABLE.items())[0][1] else:
_LOGGER.warning("Legacy configuration format detected")
for i in range(1, 5):
name_key = 'group_%d_name' % i
if name_key in bridge_conf:
groups.append({
'number': i,
'type': bridge_conf.get('group_%d_type' % i,
DEFAULT_LED_TYPE),
'name': bridge_conf.get(name_key)
})
new_bridges.append({
'host': bridge_conf.get('host'),
'groups': groups
})
return {'bridges': new_bridges}
def setup_platform(hass, config, add_devices_callback, discovery_info=None): def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Gets the LimitlessLED lights. """ """ Gets the LimitlessLED lights. """
import ledcontroller from limitlessled.bridge import Bridge
# Handle old configuration format: # Two legacy configuration formats are supported to
bridges = config.get('bridges', [config]) # maintain backwards compatibility.
config = rewrite_legacy(config)
for bridge_id, bridge in enumerate(bridges):
bridge['id'] = bridge_id
pool = ledcontroller.LedControllerPool([x['host'] for x in bridges])
# Use the expanded configuration format.
lights = [] lights = []
for bridge in bridges: for bridge_conf in config.get('bridges'):
for i in range(1, 5): bridge = Bridge(bridge_conf.get('host'),
name_key = 'group_%d_name' % i port=bridge_conf.get('port', DEFAULT_PORT),
if name_key in bridge: version=bridge_conf.get('version', DEFAULT_VERSION))
group_type = bridge.get('group_%d_type' % i, 'rgbw') for group_conf in bridge_conf.get('groups'):
lights.append(LimitlessLED.factory(pool, bridge['id'], i, group = bridge.add_group(group_conf.get('number'),
bridge[name_key], group_conf.get('name'),
group_type)) group_conf.get('type', DEFAULT_LED_TYPE))
lights.append(LimitlessLEDGroup.factory(group))
add_devices_callback(lights) add_devices_callback(lights)
class LimitlessLED(Light): def state(new_state):
""" Represents a LimitlessLED light """ """ State decorator.
Specify True (turn on) or False (turn off).
"""
def decorator(function):
""" Decorator function. """
# pylint: disable=no-member,protected-access
def wrapper(self, **kwargs):
""" Wrap a group state change. """
from limitlessled.pipeline import Pipeline
pipeline = Pipeline()
transition_time = DEFAULT_TRANSITION
# Stop any repeating pipeline.
if self.repeating:
self.repeating = False
self.group.stop()
# Not on? Turn on.
if not self.is_on:
pipeline.on()
# Set transition time.
if ATTR_TRANSITION in kwargs:
transition_time = kwargs[ATTR_TRANSITION]
# Do group type-specific work.
function(self, transition_time, pipeline, **kwargs)
# Update state.
self._is_on = new_state
self.group.enqueue(pipeline)
self.update_ha_state()
return wrapper
return decorator
class LimitlessLEDGroup(Light):
""" LimitessLED group. """
def __init__(self, group):
""" Initialize a group. """
self.group = group
self.repeating = False
self._is_on = False
self._brightness = None
@staticmethod @staticmethod
def factory(pool, controller_id, group, name, group_type): def factory(group):
''' Construct a Limitless LED of the appropriate type ''' """ Produce LimitlessLEDGroup objects. """
if group_type == 'white': from limitlessled.group.rgbw import RgbwGroup
return WhiteLimitlessLED(pool, controller_id, group, name) from limitlessled.group.white import WhiteGroup
elif group_type == 'rgbw': if isinstance(group, WhiteGroup):
return RGBWLimitlessLED(pool, controller_id, group, name) return LimitlessLEDWhiteGroup(group)
elif isinstance(group, RgbwGroup):
# pylint: disable=too-many-arguments return LimitlessLEDRGBWGroup(group)
def __init__(self, pool, controller_id, group, name, group_type):
self.pool = pool
self.controller_id = controller_id
self.group = group
self.pool.execute(self.controller_id, "set_group_type", self.group,
group_type)
# LimitlessLEDs don't report state, we have track it ourselves.
self.pool.execute(self.controller_id, "off", self.group)
self._name = name or DEVICE_DEFAULT_NAME
self._state = False
@property @property
def should_poll(self): def should_poll(self):
""" No polling needed. """ """ No polling needed.
LimitlessLED state cannot be fetched.
"""
return False return False
@property @property
def name(self): def name(self):
""" Returns the name of the device if any. """ """ Returns the name of the group. """
return self._name return self.group.name
@property @property
def is_on(self): def is_on(self):
""" True if device is on. """ """ True if device is on. """
return self._state return self._is_on
def turn_off(self, **kwargs):
""" Turn the device off. """
self._state = False
self.pool.execute(self.controller_id, "off", self.group)
self.update_ha_state()
class RGBWLimitlessLED(LimitlessLED):
""" Represents a RGBW LimitlessLED light """
def __init__(self, pool, controller_id, group, name):
super().__init__(pool, controller_id, group, name, 'rgbw')
self._brightness = 100
self._led_color = 'white'
@property @property
def brightness(self): def brightness(self):
""" Brightness property. """
return self._brightness return self._brightness
@state(False)
def turn_off(self, transition_time, pipeline, **kwargs):
""" Turn off a group. """
pipeline.transition(transition_time, brightness=0.0).off()
class LimitlessLEDWhiteGroup(LimitlessLEDGroup):
""" LimitlessLED White group. """
def __init__(self, group):
""" Initialize White group. """
super().__init__(group)
# Initialize group with known values.
self.group.on = True
self.group.temperature = 1.0
self.group.brightness = 0.0
self._brightness = _to_hass_brightness(1.0)
self._temperature = _to_hass_temperature(self.group.temperature)
self.group.on = False
@property
def color_temp(self):
""" Temperature property. """
return self._temperature
@state(True)
def turn_on(self, transition_time, pipeline, **kwargs):
""" Turn on (or adjust property of) a group. """
# Check arguments.
if ATTR_BRIGHTNESS in kwargs:
self._brightness = kwargs[ATTR_BRIGHTNESS]
if ATTR_COLOR_TEMP in kwargs:
self._temperature = kwargs[ATTR_COLOR_TEMP]
# Set up transition.
pipeline.transition(transition_time,
brightness=_from_hass_brightness(
self._brightness),
temperature=_from_hass_temperature(
self._temperature))
class LimitlessLEDRGBWGroup(LimitlessLEDGroup):
""" LimitlessLED RGBW group. """
def __init__(self, group):
""" Initialize RGBW group. """
super().__init__(group)
# Initialize group with known values.
self.group.on = True
self.group.white()
self._color = WHITE
self.group.brightness = 0.0
self._brightness = _to_hass_brightness(1.0)
self.group.on = False
@property @property
def rgb_color(self): def rgb_color(self):
return COLOR_TABLE[self._led_color] """ Color property. """
return self._color
def turn_on(self, **kwargs):
""" Turn the device on. """
self._state = True
@state(True)
def turn_on(self, transition_time, pipeline, **kwargs):
""" Turn on (or adjust property of) a group. """
from limitlessled.presets import COLORLOOP
# Check arguments.
if ATTR_BRIGHTNESS in kwargs: if ATTR_BRIGHTNESS in kwargs:
self._brightness = kwargs[ATTR_BRIGHTNESS] self._brightness = kwargs[ATTR_BRIGHTNESS]
if ATTR_RGB_COLOR in kwargs: if ATTR_RGB_COLOR in kwargs:
self._led_color = _rgb_to_led_color(kwargs[ATTR_RGB_COLOR]) self._color = kwargs[ATTR_RGB_COLOR]
# White is a special case.
effect = kwargs.get(ATTR_EFFECT) if min(self._color) > 256 - RGB_BOUNDARY:
pipeline.white()
if effect == EFFECT_COLORLOOP: self._color = WHITE
self.pool.execute(self.controller_id, "disco", self.group) # Set up transition.
elif effect == EFFECT_WHITE: pipeline.transition(transition_time,
self.pool.execute(self.controller_id, "white", self.group) brightness=_from_hass_brightness(
else: self._brightness),
self.pool.execute(self.controller_id, "set_color", color=_from_hass_color(self._color))
self._led_color, self.group) # Flash.
if ATTR_FLASH in kwargs:
# Brightness can be set independently of color duration = 0
self.pool.execute(self.controller_id, "set_brightness", if kwargs[ATTR_FLASH] == FLASH_LONG:
self._brightness / 255.0, self.group) duration = 1
pipeline.flash(duration=duration)
self.update_ha_state() # Add effects.
if ATTR_EFFECT in kwargs:
if kwargs[ATTR_EFFECT] == EFFECT_COLORLOOP:
self.repeating = True
pipeline.append(COLORLOOP)
if kwargs[ATTR_EFFECT] == EFFECT_WHITE:
pipeline.white()
self._color = WHITE
class WhiteLimitlessLED(LimitlessLED): def _from_hass_temperature(temperature):
""" Represents a White LimitlessLED light """ """ Convert Home Assistant color temperature
units to percentage.
"""
return (temperature - 154) / 346
def __init__(self, pool, controller_id, group, name):
super().__init__(pool, controller_id, group, name, 'white')
def turn_on(self, **kwargs): def _to_hass_temperature(temperature):
""" Turn the device on. """ """ Convert percentage to Home Assistant
self._state = True color temperature units.
self.pool.execute(self.controller_id, "on", self.group) """
self.update_ha_state() return int(temperature * 346) + 154
def _from_hass_brightness(brightness):
""" Convert Home Assistant brightness units
to percentage.
"""
return brightness / 255
def _to_hass_brightness(brightness):
""" Convert percentage to Home Assistant
brightness units.
"""
return int(brightness * 255)
def _from_hass_color(color):
""" Convert Home Assistant RGB list
to Color tuple.
"""
from limitlessled import Color
return Color(*tuple(color))
def _to_hass_color(color):
""" Convert from Color tuple to
Home Assistant RGB list.
"""
return list([int(c) for c in color])

View File

@ -39,7 +39,7 @@ blinkstick==1.1.7
phue==0.8 phue==0.8
# homeassistant.components.light.limitlessled # homeassistant.components.light.limitlessled
ledcontroller==1.1.0 limitlessled==1.0.0
# homeassistant.components.light.tellstick # homeassistant.components.light.tellstick
# homeassistant.components.sensor.tellstick # homeassistant.components.sensor.tellstick