diff --git a/homeassistant/components/cover/rflink.py b/homeassistant/components/cover/rflink.py new file mode 100644 index 00000000000..45a0b27aa07 --- /dev/null +++ b/homeassistant/components/cover/rflink.py @@ -0,0 +1,116 @@ +""" +Support for Rflink Cover devices. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/cover.rflink/ +""" +import asyncio +import logging + +import voluptuous as vol + +from homeassistant.components.rflink import ( + DATA_ENTITY_GROUP_LOOKUP, DATA_ENTITY_LOOKUP, + DEVICE_DEFAULTS_SCHEMA, EVENT_KEY_COMMAND, RflinkCommand) +from homeassistant.components.cover import ( + CoverDevice, PLATFORM_SCHEMA) +import homeassistant.helpers.config_validation as cv +from homeassistant.const import CONF_NAME + + +DEPENDENCIES = ['rflink'] + +_LOGGER = logging.getLogger(__name__) + + +CONF_ALIASES = 'aliases' +CONF_GROUP_ALIASES = 'group_aliases' +CONF_GROUP = 'group' +CONF_NOGROUP_ALIASES = 'nogroup_aliases' +CONF_DEVICE_DEFAULTS = 'device_defaults' +CONF_DEVICES = 'devices' +CONF_AUTOMATIC_ADD = 'automatic_add' +CONF_FIRE_EVENT = 'fire_event' +CONF_IGNORE_DEVICES = 'ignore_devices' +CONF_RECONNECT_INTERVAL = 'reconnect_interval' +CONF_SIGNAL_REPETITIONS = 'signal_repetitions' +CONF_WAIT_FOR_ACK = 'wait_for_ack' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_DEVICE_DEFAULTS, default=DEVICE_DEFAULTS_SCHEMA({})): + DEVICE_DEFAULTS_SCHEMA, + vol.Optional(CONF_DEVICES, default={}): vol.Schema({ + cv.string: { + vol.Optional(CONF_NAME): 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_FIRE_EVENT, default=False): cv.boolean, + vol.Optional(CONF_SIGNAL_REPETITIONS): vol.Coerce(int), + vol.Optional(CONF_GROUP, default=True): cv.boolean, + }, + }), +}) + + +def devices_from_config(domain_config, hass=None): + """Parse configuration and add Rflink cover devices.""" + devices = [] + for device_id, config in domain_config[CONF_DEVICES].items(): + device_config = dict(domain_config[CONF_DEVICE_DEFAULTS], **config) + device = RflinkCover(device_id, hass, **device_config) + devices.append(device) + + # Register entity (and aliases) to listen to incoming rflink events + # Device id and normal aliases respond to normal and group command + hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_COMMAND][device_id].append(device) + if config[CONF_GROUP]: + hass.data[DATA_ENTITY_GROUP_LOOKUP][ + EVENT_KEY_COMMAND][device_id].append(device) + for _id in config[CONF_ALIASES]: + hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_COMMAND][_id].append(device) + hass.data[DATA_ENTITY_GROUP_LOOKUP][ + EVENT_KEY_COMMAND][_id].append(device) + return devices + + +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + """Set up the Rflink cover platform.""" + async_add_devices(devices_from_config(config, hass)) + + +class RflinkCover(RflinkCommand, CoverDevice): + """Rflink entity which can switch on/stop/off (eg: cover).""" + + def _handle_event(self, event): + """Adjust state if Rflink picks up a remote command for this device.""" + self.cancel_queued_send_commands() + + command = event['command'] + if command in ['on', 'allon']: + self._state = True + elif command in ['off', 'alloff']: + self._state = False + + @property + def should_poll(self): + """No polling available in RFlink cover.""" + return False + + def async_close_cover(self, **kwargs): + """Turn the device close.""" + return self._async_handle_command("close_cover") + + def async_open_cover(self, **kwargs): + """Turn the device open.""" + return self._async_handle_command("open_cover") + + def async_stop_cover(self, **kwargs): + """Turn the device stop.""" + return self._async_handle_command("stop_cover") diff --git a/homeassistant/components/rflink.py b/homeassistant/components/rflink.py index 74e533d70ec..5045017790e 100644 --- a/homeassistant/components/rflink.py +++ b/homeassistant/components/rflink.py @@ -11,6 +11,7 @@ import logging import os import async_timeout + from homeassistant.config import load_yaml_config_file from homeassistant.const import ( ATTR_ENTITY_ID, CONF_COMMAND, CONF_HOST, CONF_PORT, @@ -22,6 +23,7 @@ from homeassistant.helpers.deprecation import get_deprecated from homeassistant.helpers.entity import Entity import voluptuous as vol + REQUIREMENTS = ['rflink==0.0.34'] _LOGGER = logging.getLogger(__name__) @@ -370,6 +372,19 @@ class RflinkCommand(RflinkDevice): # if the state is true, it gets set as false self._state = self._state in [STATE_UNKNOWN, False] + # Cover options for RFlink + elif command == 'close_cover': + cmd = 'DOWN' + self._state = False + + elif command == 'open_cover': + cmd = 'UP' + self._state = True + + elif command == 'stop_cover': + cmd = 'STOP' + self._state = True + # Send initial command and queue repetitions. # This allows the entity state to be updated quickly and not having to # wait for all repetitions to be sent diff --git a/tests/components/test_rflink.py b/tests/components/test_rflink.py index b4cdd96f817..e7907fc6b54 100644 --- a/tests/components/test_rflink.py +++ b/tests/components/test_rflink.py @@ -6,7 +6,8 @@ from unittest.mock import Mock from homeassistant.bootstrap import async_setup_component from homeassistant.components.rflink import ( CONF_RECONNECT_INTERVAL, SERVICE_SEND_COMMAND) -from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF +from homeassistant.const import ( + ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_STOP_COVER) from tests.common import assert_setup_component @@ -119,6 +120,38 @@ def test_send_no_wait(hass, monkeypatch): assert protocol.send_command.call_args_list[0][0][1] == 'off' +@asyncio.coroutine +def test_cover_send_no_wait(hass, monkeypatch): + """Test command sending to a cover device without ack.""" + domain = 'cover' + config = { + 'rflink': { + 'port': '/dev/ttyABC0', + 'wait_for_ack': False, + }, + domain: { + 'platform': 'rflink', + 'devices': { + 'RTS_0100F2_0': { + 'name': 'test', + 'aliases': ['test_alias_0_0'], + }, + }, + }, + } + + # setup mocking rflink module + _, _, protocol, _ = yield from mock_rflink( + hass, config, domain, monkeypatch) + + hass.async_add_job( + hass.services.async_call(domain, SERVICE_STOP_COVER, + {ATTR_ENTITY_ID: 'cover.test'})) + yield from hass.async_block_till_done() + assert protocol.send_command.call_args_list[0][0][0] == 'RTS_0100F2_0' + assert protocol.send_command.call_args_list[0][0][1] == 'STOP' + + @asyncio.coroutine def test_send_command(hass, monkeypatch): """Test send_command service."""