From 3f4f4592f2ce3a07f34c698c7d97e3411d0a2ba3 Mon Sep 17 00:00:00 2001 From: shbatm Date: Fri, 24 Apr 2020 12:31:50 -0500 Subject: [PATCH] Cleanup and Make Dependent on RFLink Cover General code cleanup per HA Standards Make RTS Cover Device class inherit from RFLink cover to prevent need for importing twice. --- rts_rflink/cover.py | 252 +++++++++++++++++++-------------------- rts_rflink/manifest.json | 1 - 2 files changed, 123 insertions(+), 130 deletions(-) diff --git a/rts_rflink/cover.py b/rts_rflink/cover.py index 404be6c..73d0131 100644 --- a/rts_rflink/cover.py +++ b/rts_rflink/cover.py @@ -1,50 +1,36 @@ """Support for RTS Cover devices over RFLink controller.""" import logging - -import voluptuous as vol - from datetime import timedelta -from homeassistant.core import callback - -from homeassistant.helpers.event import async_track_utc_time_change, async_track_time_interval -from homeassistant.components.cover import ( - ATTR_CURRENT_POSITION, - ATTR_POSITION, - PLATFORM_SCHEMA, - CoverDevice, -) -from homeassistant.const import ( - CONF_NAME, - SERVICE_CLOSE_COVER, - SERVICE_OPEN_COVER, - SERVICE_STOP_COVER, -) +import voluptuous as vol import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.restore_state import RestoreEntity - -from homeassistant.components.rflink import ( - CONF_ALIASES, - CONF_DEVICE_DEFAULTS, - CONF_DEVICES, - CONF_FIRE_EVENT, - CONF_GROUP, - CONF_GROUP_ALIASES, - CONF_NOGROUP_ALIASES, - CONF_SIGNAL_REPETITIONS, - DEVICE_DEFAULTS_SCHEMA, - EVENT_KEY_COMMAND, - RflinkCommand, -) +from homeassistant.components.cover import (ATTR_CURRENT_POSITION, + ATTR_POSITION, PLATFORM_SCHEMA, + SUPPORT_CLOSE, SUPPORT_OPEN, + SUPPORT_SET_POSITION, SUPPORT_STOP) +from homeassistant.components.rflink import (CONF_ALIASES, + CONF_DEVICE_DEFAULTS, + CONF_DEVICES, CONF_FIRE_EVENT, + CONF_GROUP, CONF_GROUP_ALIASES, + CONF_NOGROUP_ALIASES, + CONF_SIGNAL_REPETITIONS, + DEVICE_DEFAULTS_SCHEMA, + EVENT_KEY_COMMAND) +from homeassistant.components.rflink.cover import RflinkCover +from homeassistant.const import (CONF_NAME, SERVICE_CLOSE_COVER, + SERVICE_OPEN_COVER, SERVICE_STOP_COVER) +from homeassistant.core import callback +from homeassistant.helpers.event import async_track_time_interval +from xknx.devices import TravelCalculator, TravelStatus _LOGGER = logging.getLogger(__name__) PARALLEL_UPDATES = 0 -CONF_MY_POSITION = 'rts_my_position' -CONF_TRAVELLING_TIME_DOWN = 'travelling_time_down' -CONF_TRAVELLING_TIME_UP = 'travelling_time_up' +CONF_MY_POSITION = "rts_my_position" +CONF_TRAVELLING_TIME_DOWN = "travelling_time_down" +CONF_TRAVELLING_TIME_UP = "travelling_time_up" DEFAULT_TRAVEL_TIME = 25 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( @@ -56,22 +42,27 @@ { cv.string: { vol.Optional(CONF_NAME): cv.string, - vol.Optional(CONF_ALIASES, default=[]): - vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_ALIASES, default=[]): vol.All( + cv.ensure_list, [cv.string] + ), vol.Optional(CONF_GROUP_ALIASES, default=[]): vol.All( cv.ensure_list, [cv.string] ), - vol.Optional(CONF_NOGROUP_ALIASES, default=[]): - vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_NOGROUP_ALIASES, default=[]): vol.All( + cv.ensure_list, [cv.string] + ), vol.Optional(CONF_FIRE_EVENT, default=False): cv.boolean, vol.Optional(CONF_SIGNAL_REPETITIONS): vol.Coerce(int), vol.Optional(CONF_GROUP, default=True): cv.boolean, - vol.Optional(CONF_MY_POSITION): - vol.All(vol.Coerce(int), vol.Range(min=0, max=100)), - vol.Optional(CONF_TRAVELLING_TIME_DOWN, default=DEFAULT_TRAVEL_TIME): - cv.positive_int, - vol.Optional(CONF_TRAVELLING_TIME_UP, default=DEFAULT_TRAVEL_TIME): - cv.positive_int, + vol.Optional(CONF_MY_POSITION): vol.All( + vol.Coerce(int), vol.Range(min=0, max=100) + ), + vol.Optional( + CONF_TRAVELLING_TIME_DOWN, default=DEFAULT_TRAVEL_TIME + ): cv.positive_int, + vol.Optional( + CONF_TRAVELLING_TIME_UP, default=DEFAULT_TRAVEL_TIME + ): cv.positive_int, } } ), @@ -88,81 +79,86 @@ def devices_from_config(domain_config): travel_time_up = config.pop(CONF_TRAVELLING_TIME_UP) device_config = dict(domain_config[CONF_DEVICE_DEFAULTS], **config) device = RTSRflinkCover( - device_id, rts_my_position, travel_time_down, - travel_time_up, **device_config) + device_id, + rts_my_position, + travel_time_down, + travel_time_up, + **device_config + ) devices.append(device) return devices -async def async_setup_platform(hass, config, async_add_entities, - discovery_info=None): +async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the RFLink cover platform.""" async_add_entities(devices_from_config(config)) -class RTSRflinkCover(RflinkCommand, CoverDevice, RestoreEntity): - """RFLink entity which can switch on/stop/off (eg: cover).""" +class RTSRflinkCover(RflinkCover): + """RFLink entity which can switch on/stop/off (eg: cover) with time based controls.""" - def __init__(self, device_id, rts_my_position, - travel_time_down, travel_time_up, **device_config): + def __init__( + self, + device_id, + rts_my_position, + travel_time_down, + travel_time_up, + **device_config + ): """Initialize the cover.""" - from xknx.devices import TravelCalculator self._rts_my_position = rts_my_position self._travel_time_down = travel_time_down self._travel_time_up = travel_time_up - self._require_stop_cover = False -# self.async_register_callbacks() + # self.async_register_callbacks() self._unsubscribe_auto_updater = None super().__init__(device_id, None, **device_config) - self.tc = TravelCalculator( - self._travel_time_down, self._travel_time_up) + self.travel_calc = TravelCalculator( + self._travel_time_down, self._travel_time_up + ) async def async_added_to_hass(self): + """Restore RFLink cover state (OPEN/CLOSE/CURRENT_POSITION).""" await super().async_added_to_hass() - """ Only cover's position matters. """ - """ The rest is calculated from this attribute.""" + old_state = await self.async_get_last_state() - _LOGGER.debug('async_added_to_hass :: oldState %s', old_state) if ( - old_state is not None and - self.tc is not None and - old_state.attributes.get(ATTR_CURRENT_POSITION) is not None): - self.tc.set_position(int( - old_state.attributes.get(ATTR_CURRENT_POSITION))) + old_state is not None + and self.travel_calc is not None + and old_state.attributes.get(ATTR_CURRENT_POSITION) is not None + ): + self.travel_calc.set_position( + int(old_state.attributes.get(ATTR_CURRENT_POSITION)) + ) def _handle_event(self, event): """Adjust state if RFLink picks up a remote command for this device.""" self.cancel_queued_send_commands() - _LOGGER.debug('_handle_event %s', event) + _LOGGER.debug("_handle_event %s", event) # this must be wrong. ON command closes cover - # command = event['command'] command = event.get(EVENT_KEY_COMMAND) - if command in ['on', 'allon', 'up']: - self._require_stop_cover = False - self.tc.start_travel_up() + if command in ["on", "allon", "up"]: + self.travel_calc.start_travel_up() self.start_auto_updater() - elif command in ['off', 'alloff', 'down']: - self._require_stop_cover = False - self.tc.start_travel_down() + elif command in ["off", "alloff", "down"]: + self.travel_calc.start_travel_down() self.start_auto_updater() - elif command in ['stop']: + elif command in ["stop"]: self._handle_my_button() def _handle_my_button(self): - """Handle the MY button press""" - self._require_stop_cover = False - if self.tc.is_traveling(): - _LOGGER.debug('_handle_my_button :: button stops cover') - self.tc.stop() + """Handle the MY button press.""" + if self.travel_calc.is_traveling(): + _LOGGER.debug("_handle_my_button :: button stops cover") + self.travel_calc.stop() self.stop_auto_updater() elif self._rts_my_position is not None: - _LOGGER.debug('_handle_my_button :: button sends to MY') - self.tc.start_travel(self._rts_my_position) + _LOGGER.debug("_handle_my_button :: button sends to MY") + self.travel_calc.start_travel(self._rts_my_position) self.start_auto_updater() @property @@ -180,113 +176,113 @@ def device_state_attributes(self): attr[CONF_TRAVELLING_TIME_UP] = self._travel_time_up return attr + @property + def supported_features(self): + """Flag supported features.""" + return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION | SUPPORT_STOP + @property def current_cover_position(self): """Return the current position of the cover.""" - return self.tc.current_position() + return self.travel_calc.current_position() @property def is_opening(self): """Return if the cover is opening or not.""" - from xknx.devices import TravelStatus - return self.tc.is_traveling() and \ - self.tc.travel_direction == TravelStatus.DIRECTION_UP + return ( + self.travel_calc.is_traveling() + and self.travel_calc.travel_direction == TravelStatus.DIRECTION_UP + ) @property def is_closing(self): """Return if the cover is closing or not.""" - from xknx.devices import TravelStatus - return self.tc.is_traveling() and \ - self.tc.travel_direction == TravelStatus.DIRECTION_DOWN + return ( + self.travel_calc.is_traveling() + and self.travel_calc.travel_direction == TravelStatus.DIRECTION_DOWN + ) @property def is_closed(self): """Return if the cover is closed.""" - return self.tc.is_closed() - - @property - def assumed_state(self): - """Return True because covers can be stopped midway.""" - return True + return self.travel_calc.is_closed() async def async_set_cover_position(self, **kwargs): """Move the cover to a specific position.""" if ATTR_POSITION in kwargs: position = kwargs[ATTR_POSITION] - _LOGGER.debug('async_set_cover_position: %d', position) + _LOGGER.debug("async_set_cover_position: %d", position) await self.set_position(position) async def async_close_cover(self, **kwargs): """Turn the device close.""" - _LOGGER.debug('async_close_cover') - self.tc.start_travel_down() - self._require_stop_cover = False - self.start_auto_updater() + _LOGGER.debug("async_close_cover") await self._async_handle_command(SERVICE_CLOSE_COVER) + self.travel_calc.start_travel_down() + self.start_auto_updater() async def async_open_cover(self, **kwargs): """Turn the device open.""" - _LOGGER.debug('async_open_cover') - self.tc.start_travel_up() - self._require_stop_cover = False - self.start_auto_updater() + _LOGGER.debug("async_open_cover") await self._async_handle_command(SERVICE_OPEN_COVER) + self.travel_calc.start_travel_up() + self.start_auto_updater() async def async_stop_cover(self, **kwargs): """Turn the device stop.""" - _LOGGER.debug('async_stop_cover') - self._handle_my_button() + _LOGGER.debug("async_stop_cover") await self._async_handle_command(SERVICE_STOP_COVER) + self._handle_my_button() async def set_position(self, position): - _LOGGER.debug('set_position') """Move cover to a designated position.""" - current_position = self.tc.current_position() - _LOGGER.debug('set_position :: current_position: %d, new_position: %d', - current_position, position) + current_position = self.travel_calc.current_position() + _LOGGER.debug( + "set_position :: current_position: %d, new_position: %d", + current_position, + position, + ) command = None if position < current_position: command = SERVICE_CLOSE_COVER elif position > current_position: command = SERVICE_OPEN_COVER if command is not None: - self._require_stop_cover = True - self.start_auto_updater() - self.tc.start_travel(position) - _LOGGER.debug('set_position :: command %s', command) await self._async_handle_command(command) + self.start_auto_updater() + self.travel_calc.start_travel(position) + _LOGGER.debug("set_position :: command %s", command) return def start_auto_updater(self): """Start the autoupdater to update HASS while cover is moving.""" - _LOGGER.debug('start_auto_updater') + _LOGGER.debug("start_auto_updater") if self._unsubscribe_auto_updater is None: - _LOGGER.debug('init _unsubscribe_auto_updater') -# self._unsubscribe_auto_updater = async_track_utc_time_change(self.hass, self.auto_updater_hook) + _LOGGER.debug("init _unsubscribe_auto_updater") interval = timedelta(seconds=0.1) self._unsubscribe_auto_updater = async_track_time_interval( - self.hass, self.auto_updater_hook, interval) + self.hass, self.auto_updater_hook, interval + ) @callback def auto_updater_hook(self, now): """Call for the autoupdater.""" - _LOGGER.debug('auto_updater_hook') + _LOGGER.debug("auto_updater_hook") self.async_schedule_update_ha_state() if self.position_reached(): - _LOGGER.debug('auto_updater_hook :: position_reached') + _LOGGER.debug("auto_updater_hook :: position_reached") self.stop_auto_updater() self.hass.async_create_task(self.auto_stop_if_necessary()) def stop_auto_updater(self): """Stop the autoupdater.""" - _LOGGER.debug('stop_auto_updater') if self._unsubscribe_auto_updater is not None: self._unsubscribe_auto_updater() self._unsubscribe_auto_updater = None def position_reached(self): """Return if cover has reached its final position.""" - return self.tc.position_reached() + return self.travel_calc.position_reached() async def auto_stop_if_necessary(self): """Do auto stop if necessary.""" @@ -294,11 +290,9 @@ async def auto_stop_if_necessary(self): # we have to stop the device when position is reached. # unless device was traveling to fully open # or fully closed state + current_position = self.travel_calc.current_position() if self.position_reached(): - if ( - self._require_stop_cover and - not self.tc.is_closed() and - not self.tc.is_open()): - _LOGGER.debug('auto_stop_if_necessary :: calling stop command') + self.travel_calc.stop() + if 0 < current_position < 100: + _LOGGER.debug("auto_stop_if_necessary :: calling stop command") await self._async_handle_command(SERVICE_STOP_COVER) - self.tc.stop() diff --git a/rts_rflink/manifest.json b/rts_rflink/manifest.json index 70b3a06..d7742dd 100644 --- a/rts_rflink/manifest.json +++ b/rts_rflink/manifest.json @@ -3,7 +3,6 @@ "name": "RFLink", "documentation": "https://www.home-assistant.io/integrations/rflink", "requirements": [ - "rflink==0.0.52", "xknx==0.9.4" ], "codeowners": []