Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ xknx.egg-info/
.coverage
venv
.idea/
.vscode/
94 changes: 90 additions & 4 deletions home-assistant-plugin/custom_components/light/xknx.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

from custom_components.xknx import ATTR_DISCOVER_DEVICES, DATA_XKNX
from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_HS_COLOR, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS,
SUPPORT_COLOR, Light)
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, ATTR_WHITE_VALUE, PLATFORM_SCHEMA,
SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, SUPPORT_WHITE_VALUE,
Light)
from homeassistant.const import CONF_NAME
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
Expand All @@ -22,10 +23,21 @@
CONF_BRIGHTNESS_STATE_ADDRESS = 'brightness_state_address'
CONF_COLOR_ADDRESS = 'color_address'
CONF_COLOR_STATE_ADDRESS = 'color_state_address'
CONF_KELVIN_ADDRESS = 'color_temperature_address'
CONF_KELVIN_STATE_ADDRESS = 'color_temperature_state_address'
CONF_WHITE_VALUE_ADDRESS = 'white_value_address'
CONF_WHITE_VALUE_STATE_ADDRESS = 'white_value_state_address'
CONF_MIN_KELVIN = 'min_kelvin'
CONF_MAX_KELVIN = 'max_kelvin'

DEFAULT_NAME = 'XKNX Light'
DEFAULT_COLOR = [255, 255, 255]
DEFAULT_BRIGHTNESS = 255
DEFAULT_COLOR_TEMPERATURE = 333 # 3000 K
DEFAULT_MIN_MIREDS = 166 # 6000 K
DEFAULT_MAX_MIREDS = 370 # 2700 K
DEFAULT_MIN_KELVIN = 2700
DEFAULT_MAX_KELVIN = 6000
DEPENDENCIES = ['xknx']

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
Expand All @@ -36,6 +48,12 @@
vol.Optional(CONF_BRIGHTNESS_STATE_ADDRESS): cv.string,
vol.Optional(CONF_COLOR_ADDRESS): cv.string,
vol.Optional(CONF_COLOR_STATE_ADDRESS): cv.string,
vol.Optional(CONF_KELVIN_ADDRESS): cv.string,
vol.Optional(CONF_KELVIN_STATE_ADDRESS): cv.string,
vol.Optional(CONF_WHITE_VALUE_ADDRESS): cv.string,
vol.Optional(CONF_WHITE_VALUE_STATE_ADDRESS): cv.string,
vol.Optional(CONF_MIN_KELVIN): vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_MAX_KELVIN): vol.All(vol.Coerce(int), vol.Range(min=1)),
})


Expand Down Expand Up @@ -71,7 +89,13 @@ def async_add_entities_config(hass, config, async_add_entities):
group_address_brightness_state=config.get(
CONF_BRIGHTNESS_STATE_ADDRESS),
group_address_color=config.get(CONF_COLOR_ADDRESS),
group_address_color_state=config.get(CONF_COLOR_STATE_ADDRESS))
group_address_color_state=config.get(CONF_COLOR_STATE_ADDRESS),
group_address_tunable_white=config.get(CONF_WHITE_VALUE_ADDRESS),
group_address_tunable_white_state=config.get(CONF_WHITE_VALUE_STATE_ADDRESS),
group_address_color_temperature=config.get(CONF_KELVIN_ADDRESS),
group_address_color_temperature_state=config.get(CONF_KELVIN_STATE_ADDRESS),
min_kelvin=config.get(CONF_MIN_KELVIN),
max_kelvin=config.get(CONF_MAX_KELVIN))
hass.data[DATA_XKNX].xknx.devices.add(light)
async_add_entities([KNXLight(light)])

Expand Down Expand Up @@ -133,12 +157,54 @@ def hs_color(self):

@property
def color_temp(self):
"""Return the CT color temperature."""
"""Return the color temperature in mireds."""
if self.device.supports_color_temperature:
kelvin = self.device.current_color_temperature
return color_util.color_temperature_kelvin_to_mired(kelvin) \
if kelvin is not None else DEFAULT_COLOR_TEMPERATURE
return None

@property
def white_value(self):
"""Return the white value of this light between 0..255."""
if self.device.supports_tunable_white:
return self.device.current_tunable_white
return None

@property
def min_mireds(self):
"""Return the coldest color temperature this light supports in mireds."""
if self.device.supports_color_temperature:
kelvin = self.device.max_kelvin
return color_util.color_temperature_kelvin_to_mired(kelvin) \
if kelvin is not None else DEFAULT_MIN_MIREDS
return None

@property
def max_mireds(self):
"""Return the warmest color temperature this light supports in mireds."""
if self.device.supports_color_temperature:
kelvin = self.device.min_kelvin
return color_util.color_temperature_kelvin_to_mired(kelvin) \
if kelvin is not None else DEFAULT_MAX_MIREDS
return None

@property
def min_kelvin(self):
"""Return the warmest color temperature this light supports in kelvin."""
if self.device.supports_color_temperature:
kelvin = self.device.min_kelvin
return kelvin \
if kelvin is not None else DEFAULT_MIN_KELVIN
return None

@property
def max_kelvin(self):
"""Return the coldest color temperature this light supports in kelvin."""
if self.device.supports_color_temperature:
kelvin = self.device.max_kelvin
return kelvin \
if kelvin is not None else DEFAULT_MAX_KELVIN
return None

@property
Expand All @@ -164,12 +230,18 @@ def supported_features(self):
flags |= SUPPORT_BRIGHTNESS
if self.device.supports_color:
flags |= SUPPORT_COLOR | SUPPORT_BRIGHTNESS
if self.device.supports_color_temperature:
flags |= SUPPORT_COLOR_TEMP
if self.device.supports_tunable_white:
flags |= SUPPORT_WHITE_VALUE
return flags

async def async_turn_on(self, **kwargs):
"""Turn the light on."""
brightness = int(kwargs.get(ATTR_BRIGHTNESS, self.brightness))
hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_color)
mireds = kwargs.get(ATTR_COLOR_TEMP, self.color_temp)
tunable_white = int(kwargs.get(ATTR_WHITE_VALUE, self.white_value))

# fall back to default values, if required
if brightness is None:
Expand All @@ -179,6 +251,8 @@ async def async_turn_on(self, **kwargs):

update_brightness = ATTR_BRIGHTNESS in kwargs
update_color = ATTR_HS_COLOR in kwargs
update_color_temp = ATTR_COLOR_TEMP in kwargs
update_tunable_white = ATTR_WHITE_VALUE in kwargs

# always only go one path for turning on (avoid conflicting changes
# and weird effects)
Expand All @@ -193,6 +267,18 @@ async def async_turn_on(self, **kwargs):
# change RGB color (includes brightness)
await self.device.set_color(
color_util.color_hsv_to_RGB(*hs_color, brightness * 100 / 255))
elif self.device.supports_color_temperature and \
update_color_temp:
# change color temperature without ON telegram
kelvin = int(color_util.color_temperature_mired_to_kelvin(mireds))
if kelvin > self.max_kelvin:
kelvin = self.max_kelvin
elif kelvin < self.min_kelvin:
kelvin = self.min_kelvin
await self.device.set_color_temperature(kelvin)
elif self.device.supports_tunable_white and \
update_tunable_white:
await self.device.set_tunable_white(tunable_white)
else:
# no color/brightness change requested, so just turn it on
await self.device.set_on()
Expand Down
Loading