diff --git a/homeassistant/components/switch/rflink.py b/homeassistant/components/switch/rflink.py index 1abeb3eeada..58b1e0959af 100644 --- a/homeassistant/components/switch/rflink.py +++ b/homeassistant/components/switch/rflink.py @@ -9,8 +9,10 @@ import logging from homeassistant.components.rflink import ( CONF_ALIASSES, CONF_DEVICE_DEFAULTS, CONF_DEVICES, CONF_FIRE_EVENT, - CONF_SIGNAL_REPETITIONS, DATA_ENTITY_LOOKUP, DEVICE_DEFAULTS_SCHEMA, - DOMAIN, EVENT_KEY_COMMAND, SwitchableRflinkDevice, cv, vol) + CONF_GROUP, CONF_GROUP_ALIASSES, CONF_NOGROUP_ALIASSES, + CONF_SIGNAL_REPETITIONS, DATA_ENTITY_GROUP_LOOKUP, DATA_ENTITY_LOOKUP, + DEVICE_DEFAULTS_SCHEMA, DOMAIN, EVENT_KEY_COMMAND, SwitchableRflinkDevice, + cv, vol) from homeassistant.components.switch import SwitchDevice from homeassistant.const import CONF_NAME, CONF_PLATFORM @@ -27,8 +29,13 @@ PLATFORM_SCHEMA = vol.Schema({ vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_ALIASSES, default=[]): vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_GROUP_ALIASSES, default=[]): + vol.All(cv.ensure_list, [cv.string]), + vol.Optional(CONF_NOGROUP_ALIASSES, 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, }, }), }) @@ -43,9 +50,26 @@ def devices_from_config(domain_config, hass=None): devices.append(device) # Register entity (and aliasses) to listen to incoming rflink events - for _id in config[CONF_ALIASSES] + [device_id]: + # Device id and normal aliasses 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_ALIASSES]: hass.data[DATA_ENTITY_LOOKUP][ EVENT_KEY_COMMAND][_id].append(device) + hass.data[DATA_ENTITY_GROUP_LOOKUP][ + EVENT_KEY_COMMAND][_id].append(device) + # group_aliasses only respond to group commands + for _id in config[CONF_GROUP_ALIASSES]: + hass.data[DATA_ENTITY_GROUP_LOOKUP][ + EVENT_KEY_COMMAND][_id].append(device) + # nogroup_aliasses only respond to normal commands + for _id in config[CONF_NOGROUP_ALIASSES]: + hass.data[DATA_ENTITY_LOOKUP][ + EVENT_KEY_COMMAND][_id].append(device) + return devices diff --git a/tests/components/light/test_rflink.py b/tests/components/light/test_rflink.py index 7eaa8e8fc6d..0d34bb6a90f 100644 --- a/tests/components/light/test_rflink.py +++ b/tests/components/light/test_rflink.py @@ -53,7 +53,7 @@ def test_default_setup(hass, monkeypatch): assert create.call_args_list[0][1]['ignore'] # test default state of light loaded from config - light_initial = hass.states.get('light.test') + light_initial = hass.states.get(DOMAIN + '.test') assert light_initial.state == 'off' assert light_initial.attributes['assumed_state'] @@ -67,7 +67,7 @@ def test_default_setup(hass, monkeypatch): }) yield from hass.async_block_till_done() - light_after_first_command = hass.states.get('light.test') + light_after_first_command = hass.states.get(DOMAIN + '.test') assert light_after_first_command.state == 'on' # also after receiving first command state not longer has to be assumed assert 'assumed_state' not in light_after_first_command.attributes @@ -79,7 +79,7 @@ def test_default_setup(hass, monkeypatch): }) yield from hass.async_block_till_done() - assert hass.states.get('light.test').state == 'off' + assert hass.states.get(DOMAIN + '.test').state == 'off' # should repond to group command event_callback({ @@ -88,7 +88,7 @@ def test_default_setup(hass, monkeypatch): }) yield from hass.async_block_till_done() - light_after_first_command = hass.states.get('light.test') + light_after_first_command = hass.states.get(DOMAIN + '.test') assert light_after_first_command.state == 'on' # should repond to group command @@ -98,7 +98,7 @@ def test_default_setup(hass, monkeypatch): }) yield from hass.async_block_till_done() - assert hass.states.get('light.test').state == 'off' + assert hass.states.get(DOMAIN + '.test').state == 'off' # test following aliasses # mock incoming command event for this device alias @@ -108,7 +108,7 @@ def test_default_setup(hass, monkeypatch): }) yield from hass.async_block_till_done() - assert hass.states.get('light.test').state == 'on' + assert hass.states.get(DOMAIN + '.test').state == 'on' # test event for new unconfigured sensor event_callback({ @@ -117,22 +117,22 @@ def test_default_setup(hass, monkeypatch): }) yield from hass.async_block_till_done() - assert hass.states.get('light.protocol2_0_1').state == 'on' + assert hass.states.get(DOMAIN + '.protocol2_0_1').state == 'on' # test changing state from HA propagates to Rflink hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: 'light.test'})) + {ATTR_ENTITY_ID: DOMAIN + '.test'})) yield from hass.async_block_till_done() - assert hass.states.get('light.test').state == 'off' + assert hass.states.get(DOMAIN + '.test').state == 'off' assert protocol.send_command_ack.call_args_list[0][0][0] == 'protocol_0_0' assert protocol.send_command_ack.call_args_list[0][0][1] == 'off' hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: 'light.test'})) + {ATTR_ENTITY_ID: DOMAIN + '.test'})) yield from hass.async_block_till_done() - assert hass.states.get('light.test').state == 'on' + assert hass.states.get(DOMAIN + '.test').state == 'on' assert protocol.send_command_ack.call_args_list[1][0][1] == 'on' # protocols supporting dimming and on/off should create hybrid light entity @@ -143,7 +143,7 @@ def test_default_setup(hass, monkeypatch): yield from hass.async_block_till_done() hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: 'light.newkaku_0_1'})) + {ATTR_ENTITY_ID: DOMAIN + '.newkaku_0_1'})) yield from hass.async_block_till_done() # dimmable should send highest dim level when turning on @@ -155,7 +155,7 @@ def test_default_setup(hass, monkeypatch): hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: 'light.newkaku_0_1', + ATTR_ENTITY_ID: DOMAIN + '.newkaku_0_1', ATTR_BRIGHTNESS: 128, })) yield from hass.async_block_till_done() @@ -165,7 +165,7 @@ def test_default_setup(hass, monkeypatch): hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_ON, { - ATTR_ENTITY_ID: 'light.dim_test', + ATTR_ENTITY_ID: DOMAIN + '.dim_test', ATTR_BRIGHTNESS: 128, })) yield from hass.async_block_till_done() @@ -210,7 +210,7 @@ def test_firing_bus_event(hass, monkeypatch): }) yield from hass.async_block_till_done() - assert calls[0].data == {'state': 'off', 'entity_id': 'light.test'} + assert calls[0].data == {'state': 'off', 'entity_id': DOMAIN + '.test'} @asyncio.coroutine @@ -247,7 +247,7 @@ def test_signal_repetitions(hass, monkeypatch): # test if signal repetition is performed according to configuration hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: 'light.test'})) + {ATTR_ENTITY_ID: DOMAIN + '.test'})) # wait for commands and repetitions to finish yield from hass.async_block_till_done() @@ -257,7 +257,7 @@ def test_signal_repetitions(hass, monkeypatch): # test if default apply to configured devcies hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: 'light.test1'})) + {ATTR_ENTITY_ID: DOMAIN + '.test1'})) # wait for commands and repetitions to finish yield from hass.async_block_till_done() @@ -275,7 +275,7 @@ def test_signal_repetitions(hass, monkeypatch): hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: 'light.protocol_0_2'})) + {ATTR_ENTITY_ID: DOMAIN + '.protocol_0_2'})) # wait for commands and repetitions to finish yield from hass.async_block_till_done() @@ -311,10 +311,10 @@ def test_signal_repetitions_alternation(hass, monkeypatch): hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: 'light.test'})) + {ATTR_ENTITY_ID: DOMAIN + '.test'})) hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: 'light.test1'})) + {ATTR_ENTITY_ID: DOMAIN + '.test1'})) yield from hass.async_block_till_done() @@ -348,11 +348,11 @@ def test_signal_repetitions_cancelling(hass, monkeypatch): hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: 'light.test'})) + {ATTR_ENTITY_ID: DOMAIN + '.test'})) hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: 'light.test'})) + {ATTR_ENTITY_ID: DOMAIN + '.test'})) yield from hass.async_block_till_done() @@ -385,7 +385,7 @@ def test_type_toggle(hass, monkeypatch): event_callback, _, _, _ = yield from mock_rflink( hass, config, DOMAIN, monkeypatch) - assert hass.states.get('light.toggle_test').state == 'off' + assert hass.states.get(DOMAIN + '.toggle_test').state == 'off' # test sending on command to toggle alias event_callback({ @@ -394,7 +394,7 @@ def test_type_toggle(hass, monkeypatch): }) yield from hass.async_block_till_done() - assert hass.states.get('light.toggle_test').state == 'on' + assert hass.states.get(DOMAIN + '.toggle_test').state == 'on' # test sending group command to group alias event_callback({ @@ -403,7 +403,7 @@ def test_type_toggle(hass, monkeypatch): }) yield from hass.async_block_till_done() - assert hass.states.get('light.toggle_test').state == 'off' + assert hass.states.get(DOMAIN + '.toggle_test').state == 'off' @asyncio.coroutine @@ -428,7 +428,7 @@ def test_group_alias(hass, monkeypatch): event_callback, _, _, _ = yield from mock_rflink( hass, config, DOMAIN, monkeypatch) - assert hass.states.get('light.test').state == 'off' + assert hass.states.get(DOMAIN + '.test').state == 'off' # test sending group command to group alias event_callback({ @@ -437,7 +437,7 @@ def test_group_alias(hass, monkeypatch): }) yield from hass.async_block_till_done() - assert hass.states.get('light.test').state == 'on' + assert hass.states.get(DOMAIN + '.test').state == 'on' # test sending group command to group alias event_callback({ @@ -446,7 +446,7 @@ def test_group_alias(hass, monkeypatch): }) yield from hass.async_block_till_done() - assert hass.states.get('light.test').state == 'on' + assert hass.states.get(DOMAIN + '.test').state == 'on' @asyncio.coroutine @@ -471,7 +471,7 @@ def test_nogroup_alias(hass, monkeypatch): event_callback, _, _, _ = yield from mock_rflink( hass, config, DOMAIN, monkeypatch) - assert hass.states.get('light.test').state == 'off' + assert hass.states.get(DOMAIN + '.test').state == 'off' # test sending group command to nogroup alias event_callback({ @@ -480,7 +480,7 @@ def test_nogroup_alias(hass, monkeypatch): }) yield from hass.async_block_till_done() # should not affect state - assert hass.states.get('light.test').state == 'off' + assert hass.states.get(DOMAIN + '.test').state == 'off' # test sending group command to nogroup alias event_callback({ @@ -489,7 +489,7 @@ def test_nogroup_alias(hass, monkeypatch): }) yield from hass.async_block_till_done() # should affect state - assert hass.states.get('light.test').state == 'on' + assert hass.states.get(DOMAIN + '.test').state == 'on' @asyncio.coroutine @@ -514,7 +514,7 @@ def test_nogroup_device_id(hass, monkeypatch): event_callback, _, _, _ = yield from mock_rflink( hass, config, DOMAIN, monkeypatch) - assert hass.states.get('light.test').state == 'off' + assert hass.states.get(DOMAIN + '.test').state == 'off' # test sending group command to nogroup event_callback({ @@ -523,7 +523,7 @@ def test_nogroup_device_id(hass, monkeypatch): }) yield from hass.async_block_till_done() # should not affect state - assert hass.states.get('light.test').state == 'off' + assert hass.states.get(DOMAIN + '.test').state == 'off' # test sending group command to nogroup event_callback({ @@ -532,7 +532,7 @@ def test_nogroup_device_id(hass, monkeypatch): }) yield from hass.async_block_till_done() # should affect state - assert hass.states.get('light.test').state == 'on' + assert hass.states.get(DOMAIN + '.test').state == 'on' @asyncio.coroutine @@ -560,4 +560,4 @@ def test_disable_automatic_add(hass, monkeypatch): yield from hass.async_block_till_done() # make sure new device is not added - assert not hass.states.get('light.protocol_0_0') + assert not hass.states.get(DOMAIN + '.protocol_0_0') diff --git a/tests/components/switch/test_rflink.py b/tests/components/switch/test_rflink.py index f5a16e14d07..3952b6f32bc 100644 --- a/tests/components/switch/test_rflink.py +++ b/tests/components/switch/test_rflink.py @@ -86,15 +86,144 @@ def test_default_setup(hass, monkeypatch): # test changing state from HA propagates to Rflink hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: 'switch.test'})) + {ATTR_ENTITY_ID: DOMAIN + '.test'})) yield from hass.async_block_till_done() - assert hass.states.get('switch.test').state == 'off' + assert hass.states.get(DOMAIN + '.test').state == 'off' assert protocol.send_command_ack.call_args_list[0][0][0] == 'protocol_0_0' assert protocol.send_command_ack.call_args_list[0][0][1] == 'off' hass.async_add_job( hass.services.async_call(DOMAIN, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: 'switch.test'})) + {ATTR_ENTITY_ID: DOMAIN + '.test'})) yield from hass.async_block_till_done() - assert hass.states.get('switch.test').state == 'on' + assert hass.states.get(DOMAIN + '.test').state == 'on' assert protocol.send_command_ack.call_args_list[1][0][1] == 'on' + + +@asyncio.coroutine +def test_group_alias(hass, monkeypatch): + """Group aliases should only respond to group commands (allon/alloff).""" + config = { + 'rflink': { + 'port': '/dev/ttyABC0', + }, + DOMAIN: { + 'platform': 'rflink', + 'devices': { + 'protocol_0_0': { + 'name': 'test', + 'group_aliasses': ['test_group_0_0'], + }, + }, + }, + } + + # setup mocking rflink module + event_callback, _, _, _ = yield from mock_rflink( + hass, config, DOMAIN, monkeypatch) + + assert hass.states.get(DOMAIN + '.test').state == 'off' + + # test sending group command to group alias + event_callback({ + 'id': 'test_group_0_0', + 'command': 'allon', + }) + yield from hass.async_block_till_done() + + assert hass.states.get(DOMAIN + '.test').state == 'on' + + # test sending group command to group alias + event_callback({ + 'id': 'test_group_0_0', + 'command': 'off', + }) + yield from hass.async_block_till_done() + + assert hass.states.get(DOMAIN + '.test').state == 'on' + + +@asyncio.coroutine +def test_nogroup_alias(hass, monkeypatch): + """Non group aliases should not respond to group commands.""" + config = { + 'rflink': { + 'port': '/dev/ttyABC0', + }, + DOMAIN: { + 'platform': 'rflink', + 'devices': { + 'protocol_0_0': { + 'name': 'test', + 'nogroup_aliasses': ['test_nogroup_0_0'], + }, + }, + }, + } + + # setup mocking rflink module + event_callback, _, _, _ = yield from mock_rflink( + hass, config, DOMAIN, monkeypatch) + + assert hass.states.get(DOMAIN + '.test').state == 'off' + + # test sending group command to nogroup alias + event_callback({ + 'id': 'test_nogroup_0_0', + 'command': 'allon', + }) + yield from hass.async_block_till_done() + # should not affect state + assert hass.states.get(DOMAIN + '.test').state == 'off' + + # test sending group command to nogroup alias + event_callback({ + 'id': 'test_nogroup_0_0', + 'command': 'on', + }) + yield from hass.async_block_till_done() + # should affect state + assert hass.states.get(DOMAIN + '.test').state == 'on' + + +@asyncio.coroutine +def test_nogroup_device_id(hass, monkeypatch): + """Device id that do not respond to group commands (allon/alloff).""" + config = { + 'rflink': { + 'port': '/dev/ttyABC0', + }, + DOMAIN: { + 'platform': 'rflink', + 'devices': { + 'test_nogroup_0_0': { + 'name': 'test', + 'group': False, + }, + }, + }, + } + + # setup mocking rflink module + event_callback, _, _, _ = yield from mock_rflink( + hass, config, DOMAIN, monkeypatch) + + assert hass.states.get(DOMAIN + '.test').state == 'off' + + # test sending group command to nogroup + event_callback({ + 'id': 'test_nogroup_0_0', + 'command': 'allon', + }) + yield from hass.async_block_till_done() + # should not affect state + assert hass.states.get(DOMAIN + '.test').state == 'off' + + # test sending group command to nogroup + event_callback({ + 'id': 'test_nogroup_0_0', + 'command': 'on', + }) + yield from hass.async_block_till_done() + # should affect state + assert hass.states.get(DOMAIN + '.test').state == 'on'