""" Support for Homekit Cover. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/cover.homekit_controller/ """ import logging from homeassistant.components.homekit_controller import (HomeKitEntity, KNOWN_ACCESSORIES) from homeassistant.components.cover import ( CoverDevice, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_SET_POSITION, SUPPORT_OPEN_TILT, SUPPORT_CLOSE_TILT, SUPPORT_SET_TILT_POSITION, ATTR_POSITION, ATTR_TILT_POSITION) from homeassistant.const import ( STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING) STATE_STOPPED = 'stopped' DEPENDENCIES = ['homekit_controller'] _LOGGER = logging.getLogger(__name__) CURRENT_GARAGE_STATE_MAP = { 0: STATE_OPEN, 1: STATE_CLOSED, 2: STATE_OPENING, 3: STATE_CLOSING, 4: STATE_STOPPED } TARGET_GARAGE_STATE_MAP = { STATE_OPEN: 0, STATE_CLOSED: 1, STATE_STOPPED: 2 } CURRENT_WINDOW_STATE_MAP = { 0: STATE_OPENING, 1: STATE_CLOSING, 2: STATE_STOPPED } def setup_platform(hass, config, add_entities, discovery_info=None): """Set up HomeKit Cover support.""" if discovery_info is None: return accessory = hass.data[KNOWN_ACCESSORIES][discovery_info['serial']] if discovery_info['device-type'] == 'garage-door-opener': add_entities([HomeKitGarageDoorCover(accessory, discovery_info)], True) else: add_entities([HomeKitWindowCover(accessory, discovery_info)], True) class HomeKitGarageDoorCover(HomeKitEntity, CoverDevice): """Representation of a HomeKit Garage Door.""" def __init__(self, accessory, discovery_info): """Initialise the Cover.""" super().__init__(accessory, discovery_info) self._state = None self._obstruction_detected = None self.lock_state = None @property def device_class(self): """Define this cover as a garage door.""" return 'garage' def get_characteristic_types(self): """Define the homekit characteristics the entity cares about.""" # pylint: disable=import-error from homekit.model.characteristics import CharacteristicsTypes return [ CharacteristicsTypes.DOOR_STATE_CURRENT, CharacteristicsTypes.DOOR_STATE_TARGET, CharacteristicsTypes.OBSTRUCTION_DETECTED, CharacteristicsTypes.NAME, ] def _setup_name(self, char): self._name = char['value'] def _update_door_state_current(self, value): self._state = CURRENT_GARAGE_STATE_MAP[value] def _update_obstruction_detected(self, value): self._obstruction_detected = value def _update_name(self, value): self._name = value @property def available(self): """Return True if entity is available.""" return self._state is not None @property def supported_features(self): """Flag supported features.""" return SUPPORT_OPEN | SUPPORT_CLOSE @property def is_closed(self): """Return true if cover is closed, else False.""" return self._state == STATE_CLOSED @property def is_closing(self): """Return if the cover is closing or not.""" return self._state == STATE_CLOSING @property def is_opening(self): """Return if the cover is opening or not.""" return self._state == STATE_OPENING def open_cover(self, **kwargs): """Send open command.""" self.set_door_state(STATE_OPEN) def close_cover(self, **kwargs): """Send close command.""" self.set_door_state(STATE_CLOSED) def set_door_state(self, state): """Send state command.""" characteristics = [{'aid': self._aid, 'iid': self._chars['door-state.target'], 'value': TARGET_GARAGE_STATE_MAP[state]}] self.put_characteristics(characteristics) @property def device_state_attributes(self): """Return the optional state attributes.""" if self._obstruction_detected is None: return None return { 'obstruction-detected': self._obstruction_detected, } class HomeKitWindowCover(HomeKitEntity, CoverDevice): """Representation of a HomeKit Window or Window Covering.""" def __init__(self, accessory, discovery_info): """Initialise the Cover.""" super().__init__(accessory, discovery_info) self._state = None self._position = None self._tilt_position = None self._hold = None self._obstruction_detected = None self.lock_state = None @property def available(self): """Return True if entity is available.""" return self._state is not None def get_characteristic_types(self): """Define the homekit characteristics the entity cares about.""" # pylint: disable=import-error from homekit.model.characteristics import CharacteristicsTypes return [ CharacteristicsTypes.POSITION_STATE, CharacteristicsTypes.POSITION_CURRENT, CharacteristicsTypes.POSITION_TARGET, CharacteristicsTypes.POSITION_HOLD, CharacteristicsTypes.VERTICAL_TILT_CURRENT, CharacteristicsTypes.VERTICAL_TILT_TARGET, CharacteristicsTypes.HORIZONTAL_TILT_CURRENT, CharacteristicsTypes.HORIZONTAL_TILT_TARGET, CharacteristicsTypes.OBSTRUCTION_DETECTED, CharacteristicsTypes.NAME, ] def _setup_name(self, char): self._name = char['value'] def _update_position_state(self, value): self._state = CURRENT_WINDOW_STATE_MAP[value] def _update_position_current(self, value): self._position = value def _update_position_hold(self, value): self._hold = value def _update_vertical_tilt_current(self, value): self._tilt_position = value def _update_horizontal_tilt_current(self, value): self._tilt_position = value def _update_obstruction_detected(self, value): self._obstruction_detected = value def _update_name(self, value): self._hold = value @property def supported_features(self): """Flag supported features.""" supported_features = ( SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION) if self._tilt_position is not None: supported_features |= ( SUPPORT_OPEN_TILT | SUPPORT_CLOSE_TILT | SUPPORT_SET_TILT_POSITION) return supported_features @property def current_cover_position(self): """Return the current position of cover.""" return self._position @property def is_closed(self): """Return true if cover is closed, else False.""" return self._position == 0 @property def is_closing(self): """Return if the cover is closing or not.""" return self._state == STATE_CLOSING @property def is_opening(self): """Return if the cover is opening or not.""" return self._state == STATE_OPENING def open_cover(self, **kwargs): """Send open command.""" self.set_cover_position(position=100) def close_cover(self, **kwargs): """Send close command.""" self.set_cover_position(position=0) def set_cover_position(self, **kwargs): """Send position command.""" position = kwargs[ATTR_POSITION] characteristics = [{'aid': self._aid, 'iid': self._chars['position.target'], 'value': position}] self.put_characteristics(characteristics) @property def current_cover_tilt_position(self): """Return current position of cover tilt.""" return self._tilt_position def set_cover_tilt_position(self, **kwargs): """Move the cover tilt to a specific position.""" tilt_position = kwargs[ATTR_TILT_POSITION] if 'vertical-tilt.target' in self._chars: characteristics = [{'aid': self._aid, 'iid': self._chars['vertical-tilt.target'], 'value': tilt_position}] self.put_characteristics(characteristics) elif 'horizontal-tilt.target' in self._chars: characteristics = [{'aid': self._aid, 'iid': self._chars['horizontal-tilt.target'], 'value': tilt_position}] self.put_characteristics(characteristics) @property def device_state_attributes(self): """Return the optional state attributes.""" state_attributes = {} if self._obstruction_detected is not None: state_attributes['obstruction-detected'] = \ self._obstruction_detected if self._hold is not None: state_attributes['hold-position'] = \ self._hold return state_attributes