Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update KNX component to xknx 0.11 #24738

Merged
merged 8 commits into from
Jul 11, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions homeassistant/components/knx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@

TUNNELING_SCHEMA = vol.Schema({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_KNX_LOCAL_IP): cv.string,
vol.Optional(CONF_KNX_LOCAL_IP): cv.string,
vol.Optional(CONF_PORT): cv.port,
})

ROUTING_SCHEMA = vol.Schema({
vol.Required(CONF_KNX_LOCAL_IP): cv.string,
vol.Optional(CONF_KNX_LOCAL_IP): cv.string,
})

EXPOSE_SCHEMA = vol.Schema({
Expand Down Expand Up @@ -333,4 +333,13 @@ async def _async_entity_changed(self, entity_id, old_state, new_state):
"""Handle entity change."""
if new_state is None:
return
await self.device.set(float(new_state.state))
if new_state.state == "unknown":
return

if self.type == 'binary':
if new_state.state == "on":
await self.device.set(True)
elif new_state.state == "off":
await self.device.set(False)
else:
await self.device.set(new_state.state)
12 changes: 6 additions & 6 deletions homeassistant/components/knx/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,24 @@ def async_add_entities_discovery(hass, discovery_info, async_add_entities):
@callback
def async_add_entities_config(hass, config, async_add_entities):
"""Set up binary senor for KNX platform configured within platform."""
name = config.get(CONF_NAME)
name = config[CONF_NAME]
import xknx
binary_sensor = xknx.devices.BinarySensor(
hass.data[DATA_KNX].xknx,
name=name,
group_address=config.get(CONF_ADDRESS),
group_address_state=config[CONF_ADDRESS],
farmio marked this conversation as resolved.
Show resolved Hide resolved
device_class=config.get(CONF_DEVICE_CLASS),
significant_bit=config.get(CONF_SIGNIFICANT_BIT),
significant_bit=config[CONF_SIGNIFICANT_BIT],
reset_after=config.get(CONF_RESET_AFTER))
hass.data[DATA_KNX].xknx.devices.add(binary_sensor)

entity = KNXBinarySensor(binary_sensor)
automations = config.get(CONF_AUTOMATION)
if automations is not None:
for automation in automations:
counter = automation.get(CONF_COUNTER)
hook = automation.get(CONF_HOOK)
action = automation.get(CONF_ACTION)
counter = automation[CONF_COUNTER]
hook = automation[CONF_HOOK]
action = automation[CONF_ACTION]
entity.automations.append(KNXAutomation(
hass=hass, device=binary_sensor, hook=hook,
action=action, counter=counter))
Expand Down
29 changes: 15 additions & 14 deletions homeassistant/components/knx/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,18 @@

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Required(CONF_TEMPERATURE_ADDRESS): cv.string,
vol.Required(CONF_TARGET_TEMPERATURE_STATE_ADDRESS): cv.string,
vol.Optional(CONF_TARGET_TEMPERATURE_ADDRESS): cv.string,
vol.Optional(CONF_SETPOINT_SHIFT_ADDRESS): cv.string,
vol.Optional(CONF_SETPOINT_SHIFT_STATE_ADDRESS): cv.string,
vol.Optional(CONF_SETPOINT_SHIFT_STEP,
default=DEFAULT_SETPOINT_SHIFT_STEP): vol.All(
float, vol.Range(min=0, max=2)),
vol.Optional(CONF_SETPOINT_SHIFT_MAX, default=DEFAULT_SETPOINT_SHIFT_MAX):
vol.All(int, vol.Range(min=0, max=32)),
vol.Optional(CONF_SETPOINT_SHIFT_MIN, default=DEFAULT_SETPOINT_SHIFT_MIN):
vol.All(int, vol.Range(min=-32, max=0)),
vol.Required(CONF_TEMPERATURE_ADDRESS): cv.string,
vol.Required(CONF_TARGET_TEMPERATURE_STATE_ADDRESS): cv.string,
vol.Optional(CONF_TARGET_TEMPERATURE_ADDRESS): cv.string,
vol.Optional(CONF_SETPOINT_SHIFT_ADDRESS): cv.string,
vol.Optional(CONF_SETPOINT_SHIFT_STATE_ADDRESS): cv.string,
vol.Optional(CONF_OPERATION_MODE_ADDRESS): cv.string,
vol.Optional(CONF_OPERATION_MODE_STATE_ADDRESS): cv.string,
vol.Optional(CONF_CONTROLLER_STATUS_ADDRESS): cv.string,
Expand Down Expand Up @@ -112,7 +112,7 @@ def async_add_entities_config(hass, config, async_add_entities):

climate_mode = xknx.devices.ClimateMode(
hass.data[DATA_KNX].xknx,
name=config.get(CONF_NAME) + " Mode",
name=config[CONF_NAME] + " Mode",
group_address_operation_mode=config.get(CONF_OPERATION_MODE_ADDRESS),
group_address_operation_mode_state=config.get(
CONF_OPERATION_MODE_STATE_ADDRESS),
Expand All @@ -136,7 +136,7 @@ def async_add_entities_config(hass, config, async_add_entities):

climate = xknx.devices.Climate(
hass.data[DATA_KNX].xknx,
name=config.get(CONF_NAME),
name=config[CONF_NAME],
group_address_temperature=config[CONF_TEMPERATURE_ADDRESS],
group_address_target_temperature=config.get(
CONF_TARGET_TEMPERATURE_ADDRESS),
Expand All @@ -145,9 +145,9 @@ def async_add_entities_config(hass, config, async_add_entities):
group_address_setpoint_shift=config.get(CONF_SETPOINT_SHIFT_ADDRESS),
group_address_setpoint_shift_state=config.get(
CONF_SETPOINT_SHIFT_STATE_ADDRESS),
setpoint_shift_step=config.get(CONF_SETPOINT_SHIFT_STEP),
setpoint_shift_max=config.get(CONF_SETPOINT_SHIFT_MAX),
setpoint_shift_min=config.get(CONF_SETPOINT_SHIFT_MIN),
setpoint_shift_step=config[CONF_SETPOINT_SHIFT_STEP],
setpoint_shift_max=config[CONF_SETPOINT_SHIFT_MAX],
setpoint_shift_min=config[CONF_SETPOINT_SHIFT_MIN],
group_address_on_off=config.get(CONF_ON_OFF_ADDRESS),
group_address_on_off_state=config.get(CONF_ON_OFF_STATE_ADDRESS),
min_temp=config.get(CONF_MIN_TEMP),
Expand Down Expand Up @@ -211,7 +211,7 @@ def current_temperature(self):
@property
def target_temperature_step(self):
"""Return the supported step of target temperature."""
return self.device.setpoint_shift_step
return self.device.temperature_step

@property
def target_temperature(self):
Expand Down Expand Up @@ -246,9 +246,10 @@ def current_operation(self):
@property
def operation_list(self):
"""Return the list of available operation modes."""
return [OPERATION_MODES.get(operation_mode.value) for
operation_mode in
self.device.mode.operation_modes]
_operations = [OPERATION_MODES.get(operation_mode.value) for
operation_mode in
self.device.mode.operation_modes]
return list(filter(None, _operations))

async def async_set_operation_mode(self, operation_mode):
"""Set operation mode."""
Expand Down
10 changes: 5 additions & 5 deletions homeassistant/components/knx/cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,18 @@ def async_add_entities_config(hass, config, async_add_entities):
import xknx
cover = xknx.devices.Cover(
hass.data[DATA_KNX].xknx,
name=config.get(CONF_NAME),
name=config[CONF_NAME],
group_address_long=config.get(CONF_MOVE_LONG_ADDRESS),
group_address_short=config.get(CONF_MOVE_SHORT_ADDRESS),
group_address_position_state=config.get(
CONF_POSITION_STATE_ADDRESS),
group_address_angle=config.get(CONF_ANGLE_ADDRESS),
group_address_angle_state=config.get(CONF_ANGLE_STATE_ADDRESS),
group_address_position=config.get(CONF_POSITION_ADDRESS),
travel_time_down=config.get(CONF_TRAVELLING_TIME_DOWN),
travel_time_up=config.get(CONF_TRAVELLING_TIME_UP),
invert_position=config.get(CONF_INVERT_POSITION),
invert_angle=config.get(CONF_INVERT_ANGLE))
travel_time_down=config[CONF_TRAVELLING_TIME_DOWN],
travel_time_up=config[CONF_TRAVELLING_TIME_UP],
invert_position=config[CONF_INVERT_POSITION],
invert_angle=config[CONF_INVERT_ANGLE])

hass.data[DATA_KNX].xknx.devices.add(cover)
async_add_entities([KNXCover(cover)])
Expand Down
66 changes: 43 additions & 23 deletions homeassistant/components/knx/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import voluptuous as vol

from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_HS_COLOR, PLATFORM_SCHEMA,
SUPPORT_BRIGHTNESS, SUPPORT_COLOR, SUPPORT_COLOR_TEMP, 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_ADDRESS, CONF_NAME
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
Expand All @@ -21,13 +22,16 @@
CONF_COLOR_TEMP_ADDRESS = 'color_temperature_address'
CONF_COLOR_TEMP_STATE_ADDRESS = 'color_temperature_state_address'
CONF_COLOR_TEMP_MODE = 'color_temperature_mode'
CONF_RGBW_ADDRESS = 'rgbw_address'
CONF_RGBW_STATE_ADDRESS = 'rgbw_state_address'
CONF_MIN_KELVIN = 'min_kelvin'
CONF_MAX_KELVIN = 'max_kelvin'

DEFAULT_NAME = 'KNX Light'
DEFAULT_COLOR = [255, 255, 255]
DEFAULT_COLOR = (0., 0.)
DEFAULT_BRIGHTNESS = 255
DEFAULT_COLOR_TEMP_MODE = 'absolute'
DEFAULT_WHITE_VALUE = 255
DEFAULT_MIN_KELVIN = 2700 # 370 mireds
DEFAULT_MAX_KELVIN = 6000 # 166 mireds

Expand All @@ -51,6 +55,8 @@ class ColorTempModes(Enum):
vol.Optional(CONF_COLOR_TEMP_STATE_ADDRESS): cv.string,
vol.Optional(CONF_COLOR_TEMP_MODE, default=DEFAULT_COLOR_TEMP_MODE):
cv.enum(ColorTempModes),
vol.Optional(CONF_RGBW_ADDRESS): cv.string,
vol.Optional(CONF_RGBW_STATE_ADDRESS): cv.string,
vol.Optional(CONF_MIN_KELVIN, default=DEFAULT_MIN_KELVIN):
vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_MAX_KELVIN, default=DEFAULT_MAX_KELVIN):
Expand Down Expand Up @@ -105,6 +111,8 @@ def async_add_entities_config(hass, config, async_add_entities):
CONF_BRIGHTNESS_STATE_ADDRESS),
group_address_color=config.get(CONF_COLOR_ADDRESS),
group_address_color_state=config.get(CONF_COLOR_STATE_ADDRESS),
group_address_rgbw=config.get(CONF_RGBW_ADDRESS),
group_address_rgbw_state=config.get(CONF_RGBW_STATE_ADDRESS),
group_address_tunable_white=group_address_tunable_white,
group_address_tunable_white_state=group_address_tunable_white_state,
group_address_color_temperature=group_address_color_temp,
Expand Down Expand Up @@ -159,23 +167,28 @@ def should_poll(self):
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
if self.device.supports_color:
if self.device.current_color is None:
return None
return max(self.device.current_color)
if self.device.supports_brightness:
return self.device.current_brightness
if (self.device.supports_color or self.device.supports_rgbw) and \
self.device.current_color:
return max(self.device.current_color)
return None

@property
def hs_color(self):
"""Return the HS color value."""
if self.device.supports_color:
rgb = self.device.current_color
if rgb is None:
return None
return color_util.color_RGB_to_hs(*rgb)
return None
rgb = None
if self.device.supports_rgbw or self.device.supports_color:
rgb, _ = self.device.current_color
return color_util.color_RGB_to_hs(*rgb) if rgb else None

@property
def white_value(self):
"""Return the white value."""
white = None
if self.device.supports_rgbw:
_, white = self.device.current_color
return white

@property
def color_temp(self):
Expand All @@ -190,9 +203,8 @@ def color_temp(self):
# as KNX devices typically use Kelvin we use it as base for
# calculating ct from percent
return color_util.color_temperature_kelvin_to_mired(
self._min_kelvin + (
(relative_ct / 255) *
(self._max_kelvin - self._min_kelvin)))
self._min_kelvin + ((relative_ct / 255) * (
self._max_kelvin - self._min_kelvin)))
return None

@property
Expand Down Expand Up @@ -228,6 +240,8 @@ def supported_features(self):
flags |= SUPPORT_BRIGHTNESS
if self.device.supports_color:
flags |= SUPPORT_COLOR | SUPPORT_BRIGHTNESS
if self.device.supports_rgbw:
flags |= SUPPORT_COLOR | SUPPORT_WHITE_VALUE
if self.device.supports_color_temperature or \
self.device.supports_tunable_white:
flags |= SUPPORT_COLOR_TEMP
Expand All @@ -237,10 +251,12 @@ async def async_turn_on(self, **kwargs):
"""Turn the light on."""
brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness)
hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_color)
white_value = kwargs.get(ATTR_WHITE_VALUE, self.white_value)
mireds = kwargs.get(ATTR_COLOR_TEMP, self.color_temp)

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

# always only go one path for turning on (avoid conflicting changes
Expand All @@ -251,17 +267,20 @@ async def async_turn_on(self, **kwargs):
# directly if supported; don't do it if color also has to be
# changed, as RGB color implicitly sets the brightness as well
await self.device.set_brightness(brightness)
elif self.device.supports_color and \
(update_brightness or update_color):
# change RGB color (includes brightness)
elif (self.device.supports_rgbw or self.device.supports_color) and \
(update_brightness or update_color or update_white_value):
# change RGB color, white value )if supported), and brightness
# if brightness or hs_color was not yet set use the default value
# to calculate RGB from as a fallback
if brightness is None:
brightness = DEFAULT_BRIGHTNESS
if hs_color is None:
hs_color = DEFAULT_COLOR
await self.device.set_color(
color_util.color_hsv_to_RGB(*hs_color, brightness * 100 / 255))
if white_value is None and self.device.supports_rgbw:
white_value = DEFAULT_WHITE_VALUE
rgb = color_util.color_hsv_to_RGB(*hs_color,
brightness * 100 / 255)
await self.device.set_color(rgb, white_value)
elif self.device.supports_color_temperature and \
update_color_temp:
# change color temperature without ON telegram
Expand All @@ -275,13 +294,14 @@ async def async_turn_on(self, **kwargs):
update_color_temp:
# calculate relative_ct from Kelvin to fit typical KNX devices
kelvin = int(color_util.color_temperature_mired_to_kelvin(mireds))
relative_ct = int(255 * (kelvin - self._min_kelvin) /
(self._max_kelvin - self._min_kelvin))
relative_ct = int(255 * (kelvin - self._min_kelvin) / (
self._max_kelvin - self._min_kelvin))
await self.device.set_tunable_white(relative_ct)
else:
# no color/brightness change requested, so just turn it on
await self.device.set_on()

async def async_turn_off(self, **kwargs):
"""Turn the light off."""
del kwargs
farmio marked this conversation as resolved.
Show resolved Hide resolved
await self.device.set_off()
2 changes: 1 addition & 1 deletion homeassistant/components/knx/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "Knx",
"documentation": "https://www.home-assistant.io/components/knx",
"requirements": [
"xknx==0.10.0"
"xknx==0.11.0"
],
"dependencies": [],
"codeowners": [
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/knx/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ def async_get_service_config(hass, config):
import xknx
notification = xknx.devices.Notification(
hass.data[DATA_KNX].xknx,
name=config.get(CONF_NAME),
group_address=config.get(CONF_ADDRESS))
name=config[CONF_NAME],
group_address=config[CONF_ADDRESS])
hass.data[DATA_KNX].xknx.devices.add(notification)
return KNXNotificationService([notification, ])

Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/knx/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ def async_add_entities_config(hass, config, async_add_entities):
import xknx
scene = xknx.devices.Scene(
hass.data[DATA_KNX].xknx,
name=config.get(CONF_NAME),
group_address=config.get(CONF_ADDRESS),
scene_number=config.get(CONF_SCENE_NUMBER))
name=config[CONF_NAME],
group_address=config[CONF_ADDRESS],
scene_number=config[CONF_SCENE_NUMBER])
hass.data[DATA_KNX].xknx.devices.add(scene)
async_add_entities([KNXScene(scene)])

Expand Down
13 changes: 9 additions & 4 deletions homeassistant/components/knx/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_ADDRESS): cv.string,
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_TYPE): cv.string,
vol.Required(CONF_TYPE): cv.string,
})


Expand Down Expand Up @@ -42,9 +42,9 @@ def async_add_entities_config(hass, config, async_add_entities):
import xknx
sensor = xknx.devices.Sensor(
hass.data[DATA_KNX].xknx,
name=config.get(CONF_NAME),
group_address=config.get(CONF_ADDRESS),
value_type=config.get(CONF_TYPE))
name=config[CONF_NAME],
group_address_state=config[CONF_ADDRESS],
value_type=config[CONF_TYPE])
hass.data[DATA_KNX].xknx.devices.add(sensor)
async_add_entities([KNXSensor(sensor)])

Expand Down Expand Up @@ -93,6 +93,11 @@ def unit_of_measurement(self):
"""Return the unit this state is expressed in."""
return self.device.unit_of_measurement()

@property
def device_class(self):
"""Return the device class of the sensor."""
return self.device.ha_device_class()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is xknx aware of home assistant sensor device classes?

Copy link
Contributor Author

@farmio farmio Jul 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, xknx sensor classes are. The device class is returned as a string - or None if there is no fitting HA device class. From xknx/devices/remote_value_sensor.py:

@property
def ha_device_class(self):
    """Return a string representing the home assistant device class."""
    if hasattr(self.DPTMAP[self.value_type], 'ha_device_class'):
        return self.DPTMAP[self.value_type].ha_device_class
    return None

looks for ha_device_class from eg. here:
https://github.com/XKNX/xknx/blob/cc10a82d11b4a60bd9765a7b410ab05edd5da41f/xknx/knx/dpt_2byte_float.py#L87

homeassistant/const.py is not imported in xknx, if you meant that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Coupling the two packages this tightly is fragile. If the home assistant API would change, something could break until the xknx library is updated. I would recommend to have a dict in this module that maps between the xknx sensor class and the home assistant device class.

But since the xknx library seems to be built for home assistant I don't think it's strictly necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Xknx works on its own but is built with HA as a main target.
With almost 50 different sensor classes its nice to have everything in one place (range, unit of measurement, ha_device_class, ...) instead of spreading these attributes out to different modules.

We could import the DEVICE_CLASS_* constants in this module and map against our ha_device_class attribute. To me this would feel kind of redundant.


@property
def device_state_attributes(self):
"""Return the state attributes."""
Expand Down
Loading