Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for learning new commands #23888

Merged
merged 6 commits into from
Jun 5, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
27 changes: 27 additions & 0 deletions homeassistant/components/remote/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
ATTR_NUM_REPEATS = 'num_repeats'
ATTR_DELAY_SECS = 'delay_secs'
ATTR_HOLD_SECS = 'hold_secs'
ATTR_ALTERNATIVE = 'alternative'
ATTR_TIMEOUT = 'timeout'

DOMAIN = 'remote'
SCAN_INTERVAL = timedelta(seconds=30)
Expand All @@ -36,6 +38,7 @@
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)

SERVICE_SEND_COMMAND = 'send_command'
SERVICE_LEARN_COMMAND = 'learn_command'
SERVICE_SYNC = 'sync'

DEFAULT_NUM_REPEATS = 1
Expand All @@ -59,6 +62,13 @@
vol.Optional(ATTR_HOLD_SECS, default=DEFAULT_HOLD_SECS): vol.Coerce(float),
})

REMOTE_SERVICE_LEARN_COMMAND_SCHEMA = REMOTE_SERVICE_SCHEMA.extend({
vol.Optional(ATTR_DEVICE): cv.string,
vol.Optional(ATTR_COMMAND): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(ATTR_ALTERNATIVE): cv.boolean,
vol.Optional(ATTR_TIMEOUT): cv.positive_int
})


@bind_hass
def is_on(hass, entity_id=None):
Expand Down Expand Up @@ -93,6 +103,11 @@ async def async_setup(hass, config):
'async_send_command'
)

component.async_register_entity_service(
SERVICE_LEARN_COMMAND, REMOTE_SERVICE_LEARN_COMMAND_SCHEMA,
'async_learn_command'
)

return True


Expand All @@ -110,3 +125,15 @@ def async_send_command(self, command, **kwargs):
"""
return self.hass.async_add_job(ft.partial(
self.send_command, command, **kwargs))

def learn_command(self, **kwargs):
"""Learn a command from a device."""
raise NotImplementedError()

def async_learn_command(self, **kwargs):
felipediel marked this conversation as resolved.
Show resolved Hide resolved
"""Learn a command from a device.

This method must be run in the event loop and returns a coroutine.
felipediel marked this conversation as resolved.
Show resolved Hide resolved
"""
return self.hass.async_add_job(ft.partial(
felipediel marked this conversation as resolved.
Show resolved Hide resolved
self.learn_command, **kwargs))
21 changes: 20 additions & 1 deletion homeassistant/components/remote/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ turn_off:
example: 'remote.family_room'

send_command:
description: Sends a single command to a single device.
description: Sends a command or a list of commands to a device.
fields:
entity_id:
description: Name(s) of entities to send command from.
Expand All @@ -46,6 +46,25 @@ send_command:
description: An optional value that specifies that number of seconds you want to have it held before the release is send. If not specified, the release will be send immediately after the press.
example: '2.5'

learn_command:
description: Learns a command or a list of commands from a device.
fields:
entity_id:
description: Name(s) of entities to learn command from.
example: 'remote.bedroom'
device:
description: Device ID to learn command from.
example: 'television'
command:
description: A single command or a list of commands to learn.
example: 'Turn on'
alternative:
description: If code must be stored as alternative (useful for discrete remotes).
example: 'True'
timeout:
description: Timeout, in seconds, for the command to be learned.
example: '30'


harmony_sync:
description: Syncs the remote's configuration.
Expand Down
28 changes: 26 additions & 2 deletions tests/components/remote/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
components. Instead call the service directly.
"""
from homeassistant.components.remote import (
ATTR_ACTIVITY, ATTR_COMMAND, ATTR_DELAY_SECS, ATTR_DEVICE,
ATTR_NUM_REPEATS, DOMAIN, SERVICE_SEND_COMMAND)
ATTR_ACTIVITY, ATTR_ALTERNATIVE, ATTR_COMMAND, ATTR_DELAY_SECS,
ATTR_DEVICE, ATTR_NUM_REPEATS, ATTR_TIMEOUT, DOMAIN,
SERVICE_LEARN_COMMAND, SERVICE_SEND_COMMAND)
from homeassistant.const import (
ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON)
from homeassistant.loader import bind_hass
Expand Down Expand Up @@ -53,3 +54,26 @@ def send_command(hass, command, entity_id=None, device=None,
data[ATTR_DELAY_SECS] = delay_secs

hass.services.call(DOMAIN, SERVICE_SEND_COMMAND, data)


@bind_hass
def learn_command(hass, entity_id=None, device=None, command=None,
alternative=None, timeout=None):
"""Learn a command from a device."""
data = {}
if entity_id:
data[ATTR_ENTITY_ID] = entity_id

if device:
data[ATTR_DEVICE] = device

if command:
data[ATTR_COMMAND] = command

if alternative:
data[ATTR_ALTERNATIVE] = alternative

if timeout:
data[ATTR_TIMEOUT] = timeout

hass.services.call(DOMAIN, SERVICE_LEARN_COMMAND, data)
38 changes: 29 additions & 9 deletions tests/components/remote/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

TEST_PLATFORM = {remote.DOMAIN: {CONF_PLATFORM: 'test'}}
SERVICE_SEND_COMMAND = 'send_command'
SERVICE_LEARN_COMMAND = 'learn_command'


class TestRemote(unittest.TestCase):
Expand Down Expand Up @@ -53,7 +54,7 @@ def test_turn_on(self):

self.hass.block_till_done()

assert 1 == len(turn_on_calls)
assert len(turn_on_calls) == 1
call = turn_on_calls[-1]

assert remote.DOMAIN == call.domain
Expand All @@ -68,12 +69,12 @@ def test_turn_off(self):

self.hass.block_till_done()

assert 1 == len(turn_off_calls)
assert len(turn_off_calls) == 1
call = turn_off_calls[-1]

assert remote.DOMAIN == call.domain
assert SERVICE_TURN_OFF == call.service
assert 'entity_id_val' == call.data[ATTR_ENTITY_ID]
assert call.domain == remote.DOMAIN
assert call.service == SERVICE_TURN_OFF
assert call.data[ATTR_ENTITY_ID] == 'entity_id_val'

def test_send_command(self):
"""Test send_command."""
Expand All @@ -87,9 +88,28 @@ def test_send_command(self):

self.hass.block_till_done()

assert 1 == len(send_command_calls)
assert len(send_command_calls) == 1
call = send_command_calls[-1]

assert remote.DOMAIN == call.domain
assert SERVICE_SEND_COMMAND == call.service
assert 'entity_id_val' == call.data[ATTR_ENTITY_ID]
assert call.domain == remote.DOMAIN
assert call.service == SERVICE_SEND_COMMAND
assert call.data[ATTR_ENTITY_ID] == 'entity_id_val'

def test_learn_command(self):
"""Test learn_command."""
learn_command_calls = mock_service(
self.hass, remote.DOMAIN, SERVICE_LEARN_COMMAND)

common.learn_command(
self.hass, entity_id='entity_id_val',
device='test_device', command=['test_command'],
alternative=True, timeout=20)

self.hass.block_till_done()

assert len(learn_command_calls) == 1
call = learn_command_calls[-1]

assert call.domain == remote.DOMAIN
assert call.service == SERVICE_LEARN_COMMAND
assert call.data[ATTR_ENTITY_ID] == 'entity_id_val'