Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions homeassistant/components/light/rflink.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light)
from homeassistant.components.rflink import (
CONF_ALIASSES, CONF_DEVICE_DEFAULTS, CONF_DEVICES, CONF_FIRE_EVENT,
CONF_IGNORE_DEVICES, CONF_SIGNAL_REPETITIONS, DATA_DEVICE_REGISTER,
DATA_ENTITY_LOOKUP, DEVICE_DEFAULTS_SCHEMA, DOMAIN,
EVENT_KEY_COMMAND, EVENT_KEY_ID, SwitchableRflinkDevice, cv, vol)
CONF_GROUP, CONF_GROUP_ALIASSES, CONF_IGNORE_DEVICES,
CONF_NOGROUP_ALIASSES, CONF_SIGNAL_REPETITIONS, DATA_DEVICE_REGISTER,
DATA_ENTITY_GROUP_LOOKUP, DATA_ENTITY_LOOKUP, DEVICE_DEFAULTS_SCHEMA,
DOMAIN, EVENT_KEY_COMMAND, EVENT_KEY_ID, SwitchableRflinkDevice, cv, vol)
from homeassistant.const import (
CONF_NAME, CONF_PLATFORM, CONF_TYPE, STATE_UNKNOWN)

DEPENDENCIES = ['rflink']

_LOGGER = logging.getLogger(__name__)
Expand All @@ -38,8 +40,13 @@
TYPE_HYBRID, TYPE_TOGGLE),
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,
},
}),
})
Expand Down Expand Up @@ -110,7 +117,24 @@ def devices_from_config(domain_config, hass=None):
devices.append(device)

# Register entity (and aliasses) to listen to incoming rflink events
for _id in [device_id] + config[CONF_ALIASSES]:

# 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)

Expand Down
35 changes: 23 additions & 12 deletions homeassistant/components/rflink.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity

REQUIREMENTS = ['rflink==0.0.28']
REQUIREMENTS = ['rflink==0.0.31']

_LOGGER = logging.getLogger(__name__)

ATTR_EVENT = 'event'
ATTR_STATE = 'state'

CONF_ALIASSES = 'aliasses'
CONF_GROUP_ALIASSES = 'group_aliasses'
CONF_GROUP = 'group'
CONF_NOGROUP_ALIASSES = 'nogroup_aliasses'
CONF_DEVICE_DEFAULTS = 'device_defaults'
CONF_DEVICES = 'devices'
CONF_FIRE_EVENT = 'fire_event'
Expand All @@ -38,6 +41,7 @@

DATA_DEVICE_REGISTER = 'rflink_device_register'
DATA_ENTITY_LOOKUP = 'rflink_entity_lookup'
DATA_ENTITY_GROUP_LOOKUP = 'rflink_entity_group_only_lookup'
DEFAULT_RECONNECT_INTERVAL = 10
DEFAULT_SIGNAL_REPETITIONS = 1
CONNECTION_TIMEOUT = 10
Expand All @@ -48,6 +52,8 @@
EVENT_KEY_SENSOR = 'sensor'
EVENT_KEY_UNIT = 'unit'

RFLINK_GROUP_COMMANDS = ['allon', 'alloff']

DOMAIN = 'rflink'

DEVICE_DEFAULTS_SCHEMA = vol.Schema({
Expand Down Expand Up @@ -94,6 +100,10 @@ def async_setup(hass, config):
EVENT_KEY_COMMAND: defaultdict(list),
EVENT_KEY_SENSOR: defaultdict(list),
}
hass.data[DATA_ENTITY_GROUP_LOOKUP] = {
EVENT_KEY_COMMAND: defaultdict(list),
EVENT_KEY_SENSOR: defaultdict(list),
}

# Allow platform to specify function to register new unknown devices
hass.data[DATA_DEVICE_REGISTER] = {}
Expand All @@ -116,7 +126,14 @@ def event_callback(event):

# Lookup entities who registered this device id as device id or alias
event_id = event.get('id', None)
entities = hass.data[DATA_ENTITY_LOOKUP][event_type][event_id]

is_group_event = (event_type == EVENT_KEY_COMMAND and
event[EVENT_KEY_COMMAND] in RFLINK_GROUP_COMMANDS)
if is_group_event:
entities = hass.data[DATA_ENTITY_GROUP_LOOKUP][event_type].get(
event_id, [])
else:
entities = hass.data[DATA_ENTITY_LOOKUP][event_type][event_id]

if entities:
# Propagate event to every entity matching the device id
Expand Down Expand Up @@ -202,8 +219,8 @@ class RflinkDevice(Entity):
platform = None
_state = STATE_UNKNOWN

def __init__(self, device_id, hass, name=None,
aliasses=None, fire_event=False,
def __init__(self, device_id, hass, name=None, aliasses=None, group=True,
group_aliasses=None, nogroup_aliasses=None, fire_event=False,
signal_repetitions=DEFAULT_SIGNAL_REPETITIONS):
"""Initialize the device."""
self.hass = hass
Expand All @@ -215,12 +232,6 @@ def __init__(self, device_id, hass, name=None,
else:
self._name = device_id

# Generate list of device_ids to match against
if aliasses:
self._aliasses = aliasses
else:
self._aliasses = []

self._should_fire_event = fire_event
self._signal_repetitions = signal_repetitions

Expand Down Expand Up @@ -375,9 +386,9 @@ def _handle_event(self, event):
self.cancel_queued_send_commands()

command = event['command']
if command == 'on':
if command in ['on', 'allon']:
self._state = True
elif command == 'off':
elif command in ['off', 'alloff']:
self._state = False

def async_turn_on(self, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ qnapstats==0.2.3
radiotherm==1.2

# homeassistant.components.rflink
rflink==0.0.28
rflink==0.0.31

# homeassistant.components.sensor.ring
ring_doorbell==0.1.0
Expand Down
148 changes: 148 additions & 0 deletions tests/components/light/test_rflink.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ def test_default_setup(hass, monkeypatch):

assert hass.states.get('light.test').state == 'off'

# should repond to group command
event_callback({
'id': 'protocol_0_0',
'command': 'allon',
})
yield from hass.async_block_till_done()

light_after_first_command = hass.states.get('light.test')
assert light_after_first_command.state == 'on'

# should repond to group command
event_callback({
'id': 'protocol_0_0',
'command': 'alloff',
})
yield from hass.async_block_till_done()

assert hass.states.get('light.test').state == 'off'

# test following aliasses
# mock incoming command event for this device alias
event_callback({
Expand Down Expand Up @@ -385,3 +404,132 @@ def test_type_toggle(hass, monkeypatch):
yield from hass.async_block_till_done()

assert hass.states.get('light.toggle_test').state == 'off'


@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('light.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('light.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('light.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('light.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('light.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('light.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('light.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('light.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('light.test').state == 'on'