2017-06-19 05:30:39 +00:00
|
|
|
"""
|
|
|
|
Support for KNX covers.
|
|
|
|
|
2017-06-20 10:09:42 +00:00
|
|
|
For more details about this platform, please refer to the documentation at
|
|
|
|
https://home-assistant.io/components/cover.knx/
|
2017-06-19 05:30:39 +00:00
|
|
|
"""
|
|
|
|
import logging
|
|
|
|
|
|
|
|
import voluptuous as vol
|
|
|
|
|
|
|
|
from homeassistant.components.cover import (
|
2017-06-28 12:08:07 +00:00
|
|
|
CoverDevice, PLATFORM_SCHEMA, ATTR_POSITION, DEVICE_CLASSES_SCHEMA,
|
|
|
|
SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_SET_POSITION, SUPPORT_STOP,
|
|
|
|
SUPPORT_SET_TILT_POSITION
|
2017-06-19 05:30:39 +00:00
|
|
|
)
|
|
|
|
from homeassistant.components.knx import (KNXConfig, KNXMultiAddressDevice)
|
|
|
|
from homeassistant.const import (CONF_NAME, CONF_DEVICE_CLASS)
|
|
|
|
import homeassistant.helpers.config_validation as cv
|
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
CONF_GETPOSITION_ADDRESS = 'getposition_address'
|
2017-06-20 10:09:42 +00:00
|
|
|
CONF_SETPOSITION_ADDRESS = 'setposition_address'
|
2017-06-28 12:08:07 +00:00
|
|
|
CONF_GETANGLE_ADDRESS = 'getangle_address'
|
|
|
|
CONF_SETANGLE_ADDRESS = 'setangle_address'
|
2017-06-20 10:09:42 +00:00
|
|
|
CONF_STOP = 'stop_address'
|
|
|
|
CONF_UPDOWN = 'updown_address'
|
2017-06-22 11:42:13 +00:00
|
|
|
CONF_INVERT_POSITION = 'invert_position'
|
2017-06-28 12:08:07 +00:00
|
|
|
CONF_INVERT_ANGLE = 'invert_angle'
|
2017-06-19 05:30:39 +00:00
|
|
|
|
|
|
|
DEFAULT_NAME = 'KNX Cover'
|
|
|
|
DEPENDENCIES = ['knx']
|
|
|
|
|
|
|
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|
|
|
vol.Required(CONF_UPDOWN): cv.string,
|
|
|
|
vol.Required(CONF_STOP): cv.string,
|
2017-06-20 10:09:42 +00:00
|
|
|
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
|
2017-06-19 05:30:39 +00:00
|
|
|
vol.Optional(CONF_GETPOSITION_ADDRESS): cv.string,
|
|
|
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
2017-06-20 10:09:42 +00:00
|
|
|
vol.Optional(CONF_SETPOSITION_ADDRESS): cv.string,
|
2017-06-22 11:42:13 +00:00
|
|
|
vol.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
|
2017-06-28 12:08:07 +00:00
|
|
|
vol.Inclusive(CONF_GETANGLE_ADDRESS, 'angle'): cv.string,
|
|
|
|
vol.Inclusive(CONF_SETANGLE_ADDRESS, 'angle'): cv.string,
|
|
|
|
vol.Optional(CONF_INVERT_ANGLE, default=False): cv.boolean,
|
2017-06-19 05:30:39 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
|
|
|
"""Create and add an entity based on the configuration."""
|
|
|
|
add_devices([KNXCover(hass, KNXConfig(config))])
|
|
|
|
|
|
|
|
|
|
|
|
class KNXCover(KNXMultiAddressDevice, CoverDevice):
|
|
|
|
"""Representation of a KNX cover. e.g. a rollershutter."""
|
|
|
|
|
|
|
|
def __init__(self, hass, config):
|
|
|
|
"""Initialize the cover."""
|
|
|
|
KNXMultiAddressDevice.__init__(
|
|
|
|
self, hass, config,
|
|
|
|
['updown', 'stop'], # required
|
2017-06-28 12:08:07 +00:00
|
|
|
optional=['setposition', 'getposition',
|
|
|
|
'getangle', 'setangle']
|
2017-06-19 05:30:39 +00:00
|
|
|
)
|
|
|
|
self._device_class = config.config.get(CONF_DEVICE_CLASS)
|
2017-06-22 11:42:13 +00:00
|
|
|
self._invert_position = config.config.get(CONF_INVERT_POSITION)
|
2017-06-28 12:08:07 +00:00
|
|
|
self._invert_angle = config.config.get(CONF_INVERT_ANGLE)
|
2017-06-19 05:30:39 +00:00
|
|
|
self._hass = hass
|
|
|
|
self._current_pos = None
|
|
|
|
self._target_pos = None
|
2017-06-28 12:08:07 +00:00
|
|
|
self._current_tilt = None
|
|
|
|
self._target_tilt = None
|
|
|
|
self._supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | \
|
|
|
|
SUPPORT_SET_POSITION | SUPPORT_STOP
|
|
|
|
|
|
|
|
# Tilt is only supported, if there is a angle get and set address
|
|
|
|
if CONF_SETANGLE_ADDRESS in config.config:
|
|
|
|
_LOGGER.debug("%s: Tilt supported at addresses %s, %s",
|
|
|
|
self.name, config.config.get(CONF_SETANGLE_ADDRESS),
|
|
|
|
config.config.get(CONF_GETANGLE_ADDRESS))
|
|
|
|
self._supported_features = self._supported_features | \
|
|
|
|
SUPPORT_SET_TILT_POSITION
|
2017-06-19 05:30:39 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def should_poll(self):
|
|
|
|
"""Polling is needed for the KNX cover."""
|
|
|
|
return True
|
|
|
|
|
2017-06-28 12:08:07 +00:00
|
|
|
@property
|
|
|
|
def supported_features(self):
|
|
|
|
"""Flag supported features."""
|
|
|
|
return self._supported_features
|
|
|
|
|
2017-06-19 05:30:39 +00:00
|
|
|
@property
|
|
|
|
def is_closed(self):
|
|
|
|
"""Return if the cover is closed."""
|
|
|
|
if self.current_cover_position is not None:
|
|
|
|
if self.current_cover_position > 0:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
|
|
|
@property
|
|
|
|
def current_cover_position(self):
|
|
|
|
"""Return current position of cover.
|
|
|
|
|
|
|
|
None is unknown, 0 is closed, 100 is fully open.
|
|
|
|
"""
|
|
|
|
return self._current_pos
|
|
|
|
|
|
|
|
@property
|
|
|
|
def target_position(self):
|
|
|
|
"""Return the position we are trying to reach: 0 - 100."""
|
|
|
|
return self._target_pos
|
|
|
|
|
2017-06-28 12:08:07 +00:00
|
|
|
@property
|
|
|
|
def current_cover_tilt_position(self):
|
|
|
|
"""Return current position of cover.
|
|
|
|
|
|
|
|
None is unknown, 0 is closed, 100 is fully open.
|
|
|
|
"""
|
|
|
|
return self._current_tilt
|
|
|
|
|
|
|
|
@property
|
|
|
|
def target_tilt(self):
|
|
|
|
"""Return the tilt angle (in %) we are trying to reach: 0 - 100."""
|
|
|
|
return self._target_tilt
|
|
|
|
|
2017-06-19 05:30:39 +00:00
|
|
|
def set_cover_position(self, **kwargs):
|
|
|
|
"""Set new target position."""
|
|
|
|
position = kwargs.get(ATTR_POSITION)
|
|
|
|
if position is None:
|
|
|
|
return
|
|
|
|
|
2017-06-22 11:42:13 +00:00
|
|
|
if self._invert_position:
|
|
|
|
position = 100-position
|
|
|
|
|
2017-06-19 05:30:39 +00:00
|
|
|
self._target_pos = position
|
|
|
|
self.set_percentage('setposition', position)
|
2017-06-20 10:09:42 +00:00
|
|
|
_LOGGER.debug("%s: Set target position to %d", self.name, position)
|
2017-06-19 05:30:39 +00:00
|
|
|
|
|
|
|
def update(self):
|
|
|
|
"""Update device state."""
|
|
|
|
super().update()
|
|
|
|
value = self.get_percentage('getposition')
|
|
|
|
if value is not None:
|
|
|
|
self._current_pos = value
|
2017-06-22 11:42:13 +00:00
|
|
|
if self._invert_position:
|
|
|
|
self._current_pos = 100-value
|
2017-06-20 10:09:42 +00:00
|
|
|
_LOGGER.debug("%s: position = %d", self.name, value)
|
2017-06-19 05:30:39 +00:00
|
|
|
|
2017-06-28 12:08:07 +00:00
|
|
|
if self._supported_features & SUPPORT_SET_TILT_POSITION:
|
|
|
|
value = self.get_percentage('getangle')
|
|
|
|
if value is not None:
|
|
|
|
self._current_tilt = value
|
|
|
|
if self._invert_angle:
|
|
|
|
self._current_tilt = 100-value
|
|
|
|
_LOGGER.debug("%s: tilt = %d", self.name, value)
|
|
|
|
|
2017-06-19 05:30:39 +00:00
|
|
|
def open_cover(self, **kwargs):
|
|
|
|
"""Open the cover."""
|
|
|
|
_LOGGER.debug("%s: open: updown = 0", self.name)
|
|
|
|
self.set_int_value('updown', 0)
|
|
|
|
|
|
|
|
def close_cover(self, **kwargs):
|
|
|
|
"""Close the cover."""
|
|
|
|
_LOGGER.debug("%s: open: updown = 1", self.name)
|
|
|
|
self.set_int_value('updown', 1)
|
|
|
|
|
|
|
|
def stop_cover(self, **kwargs):
|
|
|
|
"""Stop the cover movement."""
|
|
|
|
_LOGGER.debug("%s: stop: stop = 1", self.name)
|
|
|
|
self.set_int_value('stop', 1)
|
|
|
|
|
2017-06-28 12:08:07 +00:00
|
|
|
def set_cover_tilt_position(self, tilt_position, **kwargs):
|
|
|
|
"""Move the cover til to a specific position."""
|
|
|
|
if self._invert_angle:
|
|
|
|
tilt_position = 100-tilt_position
|
|
|
|
|
|
|
|
self._target_tilt = round(tilt_position, -1)
|
|
|
|
self.set_percentage('setangle', tilt_position)
|
|
|
|
|
2017-06-19 05:30:39 +00:00
|
|
|
@property
|
|
|
|
def device_class(self):
|
|
|
|
"""Return the class of this device, from component DEVICE_CLASSES."""
|
|
|
|
return self._device_class
|