diff --git a/homeassistant/components/broadlink/remote.py b/homeassistant/components/broadlink/remote.py index cd86244cea8..5d0d618d7be 100644 --- a/homeassistant/components/broadlink/remote.py +++ b/homeassistant/components/broadlink/remote.py @@ -18,6 +18,7 @@ import voluptuous as vol from homeassistant.components.remote import ( ATTR_ALTERNATIVE, ATTR_COMMAND, + ATTR_COMMAND_TYPE, ATTR_DELAY_SECS, ATTR_DEVICE, ATTR_NUM_REPEATS, @@ -41,6 +42,10 @@ _LOGGER = logging.getLogger(__name__) LEARNING_TIMEOUT = timedelta(seconds=30) +COMMAND_TYPE_IR = "ir" +COMMAND_TYPE_RF = "rf" +COMMAND_TYPES = [COMMAND_TYPE_IR, COMMAND_TYPE_RF] + CODE_STORAGE_VERSION = 1 FLAG_STORAGE_VERSION = 1 FLAG_SAVE_DELAY = 15 @@ -64,6 +69,7 @@ SERVICE_SEND_SCHEMA = COMMAND_SCHEMA.extend( SERVICE_LEARN_SCHEMA = COMMAND_SCHEMA.extend( { vol.Required(ATTR_DEVICE): vol.All(cv.string, vol.Length(min=1)), + vol.Optional(ATTR_COMMAND_TYPE, default=COMMAND_TYPE_IR): vol.In(COMMAND_TYPES), vol.Optional(ATTR_ALTERNATIVE, default=False): cv.boolean, } ) @@ -266,11 +272,11 @@ class BroadlinkRemote(RemoteEntity, RestoreEntity): await self._device.async_request(self._device.api.send_data, code) except (AuthorizationError, NetworkTimeoutError, OSError) as err: - _LOGGER.error("Failed to send '%s': %s", command, err) + _LOGGER.error("Failed to send '%s': %s", cmd, err) break except BroadlinkException as err: - _LOGGER.error("Failed to send '%s': %s", command, err) + _LOGGER.error("Failed to send '%s': %s", cmd, err) should_delay = False continue @@ -284,6 +290,7 @@ class BroadlinkRemote(RemoteEntity, RestoreEntity): """Learn a list of commands from a remote.""" kwargs = SERVICE_LEARN_SCHEMA(kwargs) commands = kwargs[ATTR_COMMAND] + command_type = kwargs[ATTR_COMMAND_TYPE] device = kwargs[ATTR_DEVICE] toggle = kwargs[ATTR_ALTERNATIVE] @@ -293,13 +300,18 @@ class BroadlinkRemote(RemoteEntity, RestoreEntity): ) return + if command_type == COMMAND_TYPE_IR: + learn_command = self._async_learn_ir_command + else: + learn_command = self._async_learn_rf_command + should_store = False for command in commands: try: - code = await self._async_learn_command(command) + code = await learn_command(command) if toggle: - code = [code, await self._async_learn_command(command)] + code = [code, await learn_command(command)] except (AuthorizationError, NetworkTimeoutError, OSError) as err: _LOGGER.error("Failed to learn '%s': %s", command, err) @@ -315,8 +327,8 @@ class BroadlinkRemote(RemoteEntity, RestoreEntity): if should_store: await self._code_storage.async_save(self._codes) - async def _async_learn_command(self, command): - """Learn a command from a remote.""" + async def _async_learn_ir_command(self, command): + """Learn an infrared command.""" try: await self._device.async_request(self._device.api.enter_learning) @@ -336,12 +348,87 @@ class BroadlinkRemote(RemoteEntity, RestoreEntity): await asyncio.sleep(1) try: code = await self._device.async_request(self._device.api.check_data) - except (ReadError, StorageError): continue - return b64encode(code).decode("utf8") - raise TimeoutError("No code received") + + raise TimeoutError( + "No infrared code received within " + f"{LEARNING_TIMEOUT.seconds} seconds" + ) + + finally: + self.hass.components.persistent_notification.async_dismiss( + notification_id="learn_command" + ) + + async def _async_learn_rf_command(self, command): + """Learn a radiofrequency command.""" + try: + await self._device.async_request(self._device.api.sweep_frequency) + + except (BroadlinkException, OSError) as err: + _LOGGER.debug("Failed to sweep frequency: %s", err) + raise + + self.hass.components.persistent_notification.async_create( + f"Press and hold the '{command}' button.", + title="Sweep frequency", + notification_id="sweep_frequency", + ) + + try: + start_time = utcnow() + while (utcnow() - start_time) < LEARNING_TIMEOUT: + await asyncio.sleep(1) + found = await self._device.async_request( + self._device.api.check_frequency + ) + if found: + break + else: + await self._device.async_request( + self._device.api.cancel_sweep_frequency + ) + raise TimeoutError( + "No radiofrequency found within " + f"{LEARNING_TIMEOUT.seconds} seconds" + ) + + finally: + self.hass.components.persistent_notification.async_dismiss( + notification_id="sweep_frequency" + ) + + await asyncio.sleep(1) + + try: + await self._device.async_request(self._device.api.find_rf_packet) + + except (BroadlinkException, OSError) as err: + _LOGGER.debug("Failed to enter learning mode: %s", err) + raise + + self.hass.components.persistent_notification.async_create( + f"Press the '{command}' button again.", + title="Learn command", + notification_id="learn_command", + ) + + try: + start_time = utcnow() + while (utcnow() - start_time) < LEARNING_TIMEOUT: + await asyncio.sleep(1) + try: + code = await self._device.async_request(self._device.api.check_data) + except (ReadError, StorageError): + continue + return b64encode(code).decode("utf8") + + raise TimeoutError( + "No radiofrequency code received within " + f"{LEARNING_TIMEOUT.seconds} seconds" + ) finally: self.hass.components.persistent_notification.async_dismiss(