Upgrading to python-wink 0.7.4 and improving RGB color support in HA (#1832)
This commit is contained in:
parent
bb439129e6
commit
09693bf16c
11 changed files with 195 additions and 15 deletions
|
@ -10,7 +10,7 @@ from homeassistant.components.binary_sensor import BinarySensorDevice
|
|||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
REQUIREMENTS = ['python-wink==0.6.4']
|
||||
REQUIREMENTS = ['python-wink==0.7.4']
|
||||
|
||||
# These are the available sensors mapped to binary_sensor class
|
||||
SENSOR_TYPES = {
|
||||
|
|
|
@ -9,7 +9,7 @@ import logging
|
|||
from homeassistant.components.garage_door import GarageDoorDevice
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
|
||||
REQUIREMENTS = ['python-wink==0.6.4']
|
||||
REQUIREMENTS = ['python-wink==0.7.4']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
|
|
|
@ -209,6 +209,8 @@ class LIFXLight(Light):
|
|||
brightness = self._bri
|
||||
|
||||
if ATTR_COLOR_TEMP in kwargs:
|
||||
# pylint: disable=fixme
|
||||
# TODO: Use color_temperature_mired_to_kelvin from util.color
|
||||
kelvin = int(((TEMP_MAX - TEMP_MIN) *
|
||||
(kwargs[ATTR_COLOR_TEMP] - TEMP_MIN_HASS) /
|
||||
(TEMP_MAX_HASS - TEMP_MIN_HASS)) + TEMP_MIN)
|
||||
|
|
|
@ -6,10 +6,14 @@ https://home-assistant.io/components/light.wink/
|
|||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, Light
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, \
|
||||
Light, ATTR_RGB_COLOR
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
from homeassistant.util import color as color_util
|
||||
from homeassistant.util.color import \
|
||||
color_temperature_mired_to_kelvin as mired_to_kelvin
|
||||
|
||||
REQUIREMENTS = ['python-wink==0.6.4']
|
||||
REQUIREMENTS = ['python-wink==0.7.4']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
|
@ -35,7 +39,11 @@ class WinkLight(Light):
|
|||
"""Representation of a Wink light."""
|
||||
|
||||
def __init__(self, wink):
|
||||
"""Initialize the light."""
|
||||
"""
|
||||
Initialize the light.
|
||||
|
||||
:type wink: pywink.devices.standard.bulb.WinkBulb
|
||||
"""
|
||||
self.wink = wink
|
||||
|
||||
@property
|
||||
|
@ -63,15 +71,41 @@ class WinkLight(Light):
|
|||
"""True if connection == True."""
|
||||
return self.wink.available
|
||||
|
||||
@property
|
||||
def xy_color(self):
|
||||
"""Current bulb color in CIE 1931 (XY) color space."""
|
||||
if not self.wink.supports_xy_color():
|
||||
return None
|
||||
return self.wink.color_xy()
|
||||
|
||||
@property
|
||||
def color_temp(self):
|
||||
"""Current bulb color in degrees Kelvin."""
|
||||
if not self.wink.supports_temperature():
|
||||
return None
|
||||
return color_util.color_temperature_kelvin_to_mired(
|
||||
self.wink.color_temperature_kelvin())
|
||||
|
||||
# pylint: disable=too-few-public-methods
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the switch on."""
|
||||
brightness = kwargs.get(ATTR_BRIGHTNESS)
|
||||
rgb_color = kwargs.get(ATTR_RGB_COLOR)
|
||||
color_temp_mired = kwargs.get(ATTR_COLOR_TEMP)
|
||||
|
||||
if brightness is not None:
|
||||
self.wink.set_state(True, brightness=brightness / 255)
|
||||
else:
|
||||
self.wink.set_state(True)
|
||||
state_kwargs = {
|
||||
}
|
||||
|
||||
if rgb_color:
|
||||
state_kwargs['color_xy'] = color_util.color_RGB_to_xy(*rgb_color)
|
||||
|
||||
if color_temp_mired:
|
||||
state_kwargs['color_kelvin'] = mired_to_kelvin(color_temp_mired)
|
||||
|
||||
if brightness:
|
||||
state_kwargs['brightness'] = brightness / 255.0
|
||||
|
||||
self.wink.set_state(True, **state_kwargs)
|
||||
|
||||
def turn_off(self):
|
||||
"""Turn the switch off."""
|
||||
|
@ -79,4 +113,4 @@ class WinkLight(Light):
|
|||
|
||||
def update(self):
|
||||
"""Update state of the light."""
|
||||
self.wink.update_state()
|
||||
self.wink.update_state(require_desired_state_fulfilled=True)
|
||||
|
|
|
@ -9,7 +9,7 @@ import logging
|
|||
from homeassistant.components.lock import LockDevice
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
|
||||
REQUIREMENTS = ['python-wink==0.6.4']
|
||||
REQUIREMENTS = ['python-wink==0.7.4']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
|
|
|
@ -10,7 +10,7 @@ from homeassistant.const import (CONF_ACCESS_TOKEN, STATE_CLOSED,
|
|||
STATE_OPEN, TEMP_CELCIUS)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
REQUIREMENTS = ['python-wink==0.6.4']
|
||||
REQUIREMENTS = ['python-wink==0.7.4']
|
||||
|
||||
SENSOR_TYPES = ['temperature', 'humidity']
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import logging
|
|||
from homeassistant.components.wink import WinkToggleDevice
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
|
||||
REQUIREMENTS = ['python-wink==0.6.4']
|
||||
REQUIREMENTS = ['python-wink==0.7.4']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
|
|
|
@ -15,7 +15,7 @@ from homeassistant.helpers.entity import ToggleEntity
|
|||
from homeassistant.loader import get_component
|
||||
|
||||
DOMAIN = "wink"
|
||||
REQUIREMENTS = ['python-wink==0.6.4']
|
||||
REQUIREMENTS = ['python-wink==0.7.4']
|
||||
|
||||
DISCOVER_LIGHTS = "wink.lights"
|
||||
DISCOVER_SWITCHES = "wink.switches"
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
"""Color util methods."""
|
||||
import math
|
||||
|
||||
HASS_COLOR_MAX = 500 # mireds (inverted)
|
||||
HASS_COLOR_MIN = 154
|
||||
|
||||
|
||||
# Taken from: http://www.cse.unr.edu/~quiroz/inc/colortransforms.py
|
||||
|
@ -90,3 +94,76 @@ def rgb_hex_to_rgb_list(hex_string):
|
|||
for i in range(0,
|
||||
len(hex_string),
|
||||
len(hex_string) // 3)]
|
||||
|
||||
|
||||
def color_temperature_to_rgb(color_temperature_kelvin):
|
||||
"""
|
||||
Return an RGB color from a color temperature in Kelvin.
|
||||
|
||||
This is a rough approximation based on the formula provided by T. Helland
|
||||
http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
|
||||
"""
|
||||
# range check
|
||||
if color_temperature_kelvin < 1000:
|
||||
color_temperature_kelvin = 1000
|
||||
elif color_temperature_kelvin > 40000:
|
||||
color_temperature_kelvin = 40000
|
||||
|
||||
tmp_internal = color_temperature_kelvin / 100.0
|
||||
|
||||
red = _get_red(tmp_internal)
|
||||
|
||||
green = _get_green(tmp_internal)
|
||||
|
||||
blue = _get_blue(tmp_internal)
|
||||
|
||||
return (red, green, blue)
|
||||
|
||||
|
||||
def _bound(color_component, minimum=0, maximum=255):
|
||||
"""
|
||||
Bound the given color component value between the given min and max values.
|
||||
|
||||
The minimum and maximum values will be included in the valid output.
|
||||
i.e. Given a color_component of 0 and a minimum of 10, the returned value
|
||||
will be 10.
|
||||
"""
|
||||
color_component_out = max(color_component, minimum)
|
||||
return min(color_component_out, maximum)
|
||||
|
||||
|
||||
def _get_red(temperature):
|
||||
"""Get the red component of the temperature in RGB space."""
|
||||
if temperature <= 66:
|
||||
return 255
|
||||
tmp_red = 329.698727446 * math.pow(temperature - 60, -0.1332047592)
|
||||
return _bound(tmp_red)
|
||||
|
||||
|
||||
def _get_green(temperature):
|
||||
"""Get the green component of the given color temp in RGB space."""
|
||||
if temperature <= 66:
|
||||
green = 99.4708025861 * math.log(temperature) - 161.1195681661
|
||||
else:
|
||||
green = 288.1221695283 * math.pow(temperature - 60, -0.0755148492)
|
||||
return _bound(green)
|
||||
|
||||
|
||||
def _get_blue(tmp_internal):
|
||||
"""Get the blue component of the given color temperature in RGB space."""
|
||||
if tmp_internal >= 66:
|
||||
return 255
|
||||
if tmp_internal <= 19:
|
||||
return 0
|
||||
blue = 138.5177312231 * math.log(tmp_internal - 10) - 305.0447927307
|
||||
return _bound(blue)
|
||||
|
||||
|
||||
def color_temperature_mired_to_kelvin(mired_temperature):
|
||||
"""Convert absolute mired shift to degrees kelvin."""
|
||||
return 1000000 / mired_temperature
|
||||
|
||||
|
||||
def color_temperature_kelvin_to_mired(kelvin_temperature):
|
||||
"""Convert degrees kelvin to mired shift."""
|
||||
return 1000000 / kelvin_temperature
|
||||
|
|
|
@ -242,7 +242,7 @@ python-twitch==1.2.0
|
|||
# homeassistant.components.lock.wink
|
||||
# homeassistant.components.sensor.wink
|
||||
# homeassistant.components.switch.wink
|
||||
python-wink==0.6.4
|
||||
python-wink==0.7.4
|
||||
|
||||
# homeassistant.components.keyboard
|
||||
pyuserinput==0.1.9
|
||||
|
|
|
@ -57,3 +57,70 @@ class TestColorUtil(unittest.TestCase):
|
|||
|
||||
self.assertEqual([51, 153, 255, 0],
|
||||
color_util.rgb_hex_to_rgb_list('3399ff00'))
|
||||
|
||||
|
||||
class ColorTemperatureMiredToKelvinTests(unittest.TestCase):
|
||||
"""Test color_temperature_mired_to_kelvin."""
|
||||
|
||||
def test_should_return_25000_kelvin_when_input_is_40_mired(self):
|
||||
"""Function should return 25000K if given 40 mired."""
|
||||
kelvin = color_util.color_temperature_mired_to_kelvin(40)
|
||||
self.assertEqual(25000, kelvin)
|
||||
|
||||
def test_should_return_5000_kelvin_when_input_is_200_mired(self):
|
||||
"""Function should return 5000K if given 200 mired."""
|
||||
kelvin = color_util.color_temperature_mired_to_kelvin(200)
|
||||
self.assertEqual(5000, kelvin)
|
||||
|
||||
|
||||
class ColorTemperatureKelvinToMiredTests(unittest.TestCase):
|
||||
"""Test color_temperature_kelvin_to_mired."""
|
||||
|
||||
def test_should_return_40_mired_when_input_is_25000_kelvin(self):
|
||||
"""Function should return 40 mired when given 25000 Kelvin."""
|
||||
mired = color_util.color_temperature_kelvin_to_mired(25000)
|
||||
self.assertEqual(40, mired)
|
||||
|
||||
def test_should_return_200_mired_when_input_is_5000_kelvin(self):
|
||||
"""Function should return 200 mired when given 5000 Kelvin."""
|
||||
mired = color_util.color_temperature_kelvin_to_mired(5000)
|
||||
self.assertEqual(200, mired)
|
||||
|
||||
|
||||
class ColorTemperatureToRGB(unittest.TestCase):
|
||||
"""Test color_temperature_to_rgb."""
|
||||
|
||||
def test_returns_same_value_for_any_two_temperatures_below_1000(self):
|
||||
"""Function should return same value for 999 Kelvin and 0 Kelvin."""
|
||||
rgb_1 = color_util.color_temperature_to_rgb(999)
|
||||
rgb_2 = color_util.color_temperature_to_rgb(0)
|
||||
self.assertEqual(rgb_1, rgb_2)
|
||||
|
||||
def test_returns_same_value_for_any_two_temperatures_above_40000(self):
|
||||
"""Function should return same value for 40001K and 999999K."""
|
||||
rgb_1 = color_util.color_temperature_to_rgb(40001)
|
||||
rgb_2 = color_util.color_temperature_to_rgb(999999)
|
||||
self.assertEqual(rgb_1, rgb_2)
|
||||
|
||||
def test_should_return_pure_white_at_6600(self):
|
||||
"""
|
||||
Function should return red=255, blue=255, green=255 when given 6600K.
|
||||
|
||||
6600K is considered "pure white" light.
|
||||
This is just a rough estimate because the formula itself is a "best
|
||||
guess" approach.
|
||||
"""
|
||||
rgb = color_util.color_temperature_to_rgb(6600)
|
||||
self.assertEqual((255, 255, 255), rgb)
|
||||
|
||||
def test_color_above_6600_should_have_more_blue_than_red_or_green(self):
|
||||
"""Function should return a higher blue value for blue-ish light."""
|
||||
rgb = color_util.color_temperature_to_rgb(6700)
|
||||
self.assertGreater(rgb[2], rgb[1])
|
||||
self.assertGreater(rgb[2], rgb[0])
|
||||
|
||||
def test_color_below_6600_should_have_more_red_than_blue_or_green(self):
|
||||
"""Function should return a higher red value for red-ish light."""
|
||||
rgb = color_util.color_temperature_to_rgb(6500)
|
||||
self.assertGreater(rgb[0], rgb[1])
|
||||
self.assertGreater(rgb[0], rgb[2])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue