Clean up command learning in the Broadlink integration (#36318)

This commit is contained in:
Felipe Martins Diel 2020-06-09 09:15:46 -03:00 committed by GitHub
parent 7cc3102209
commit 38bb8ef4d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 59 deletions

View File

@ -2,7 +2,6 @@
import asyncio import asyncio
from base64 import b64decode, b64encode from base64 import b64decode, b64encode
from binascii import unhexlify from binascii import unhexlify
from datetime import timedelta
import logging import logging
import re import re
@ -13,7 +12,7 @@ from homeassistant.const import CONF_HOST
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from .const import CONF_PACKET, DOMAIN, SERVICE_LEARN, SERVICE_SEND from .const import CONF_PACKET, DOMAIN, LEARNING_TIMEOUT, SERVICE_LEARN, SERVICE_SEND
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -84,7 +83,7 @@ async def async_setup_service(hass, host, device):
_LOGGER.info("Press the key you want Home Assistant to learn") _LOGGER.info("Press the key you want Home Assistant to learn")
start_time = utcnow() start_time = utcnow()
while (utcnow() - start_time) < timedelta(seconds=20): while (utcnow() - start_time) < LEARNING_TIMEOUT:
await asyncio.sleep(1) await asyncio.sleep(1)
try: try:
packet = await device.async_request(device.api.check_data) packet = await device.async_request(device.api.check_data)

View File

@ -1,7 +1,8 @@
"""Constants for broadlink platform.""" """Constants for broadlink platform."""
from datetime import timedelta
CONF_PACKET = "packet" CONF_PACKET = "packet"
DEFAULT_LEARNING_TIMEOUT = 20
DEFAULT_NAME = "Broadlink" DEFAULT_NAME = "Broadlink"
DEFAULT_PORT = 80 DEFAULT_PORT = 80
DEFAULT_RETRY = 3 DEFAULT_RETRY = 3
@ -9,6 +10,8 @@ DEFAULT_TIMEOUT = 5
DOMAIN = "broadlink" DOMAIN = "broadlink"
LEARNING_TIMEOUT = timedelta(seconds=30)
SERVICE_LEARN = "learn" SERVICE_LEARN = "learn"
SERVICE_SEND = "send" SERVICE_SEND = "send"

View File

@ -24,7 +24,6 @@ from homeassistant.components.remote import (
ATTR_DELAY_SECS, ATTR_DELAY_SECS,
ATTR_DEVICE, ATTR_DEVICE,
ATTR_NUM_REPEATS, ATTR_NUM_REPEATS,
ATTR_TIMEOUT,
DEFAULT_DELAY_SECS, DEFAULT_DELAY_SECS,
DOMAIN as COMPONENT, DOMAIN as COMPONENT,
PLATFORM_SCHEMA, PLATFORM_SCHEMA,
@ -40,10 +39,10 @@ from homeassistant.util.dt import utcnow
from . import DOMAIN, data_packet, hostname, mac_address from . import DOMAIN, data_packet, hostname, mac_address
from .const import ( from .const import (
DEFAULT_LEARNING_TIMEOUT,
DEFAULT_NAME, DEFAULT_NAME,
DEFAULT_PORT, DEFAULT_PORT,
DEFAULT_TIMEOUT, DEFAULT_TIMEOUT,
LEARNING_TIMEOUT,
RM4_TYPES, RM4_TYPES,
RM_TYPES, RM_TYPES,
) )
@ -74,10 +73,7 @@ SERVICE_SEND_SCHEMA = MINIMUM_SERVICE_SCHEMA.extend(
) )
SERVICE_LEARN_SCHEMA = MINIMUM_SERVICE_SCHEMA.extend( SERVICE_LEARN_SCHEMA = MINIMUM_SERVICE_SCHEMA.extend(
{ {vol.Optional(ATTR_ALTERNATIVE, default=False): cv.boolean}
vol.Optional(ATTR_ALTERNATIVE, default=False): cv.boolean,
vol.Optional(ATTR_TIMEOUT, default=DEFAULT_LEARNING_TIMEOUT): cv.positive_int,
}
) )
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
@ -267,7 +263,6 @@ class BroadlinkRemote(RemoteEntity):
commands = kwargs[ATTR_COMMAND] commands = kwargs[ATTR_COMMAND]
device = kwargs[ATTR_DEVICE] device = kwargs[ATTR_DEVICE]
toggle = kwargs[ATTR_ALTERNATIVE] toggle = kwargs[ATTR_ALTERNATIVE]
timeout = kwargs[ATTR_TIMEOUT]
if not self._state: if not self._state:
return return
@ -275,66 +270,47 @@ class BroadlinkRemote(RemoteEntity):
should_store = False should_store = False
for command in commands: for command in commands:
try: try:
should_store |= await self._async_learn_code( code = await self._async_learn_command(command)
command, device, toggle, timeout if toggle:
) code = [code, await self._async_learn_command(command)]
except (AuthorizationError, DeviceOfflineError): except (AuthorizationError, DeviceOfflineError) as err_msg:
_LOGGER.error("Failed to learn '%s': %s", command, err_msg)
break break
except BroadlinkException: except (BroadlinkException, TimeoutError) as err_msg:
pass _LOGGER.error("Failed to learn '%s': %s", command, err_msg)
continue
else:
self._codes.setdefault(device, {}).update({command: code})
should_store = True
if should_store: if should_store:
await self._code_storage.async_save(self._codes) await self._code_storage.async_save(self._codes)
async def _async_learn_code(self, command, device, toggle, timeout): async def _async_learn_command(self, command):
"""Learn a code from a remote. """Learn a command from a remote."""
Capture an additional code for toggle commands.
"""
try: try:
if not toggle: await self.device.async_request(self.device.api.enter_learning)
code = await self._async_capture_code(command, timeout)
else:
code = [
await self._async_capture_code(command, timeout),
await self._async_capture_code(command, timeout),
]
except TimeoutError:
_LOGGER.error("Failed to learn '%s/%s': No code received", command, device)
return False
except BroadlinkException as err_msg: except BroadlinkException as err_msg:
_LOGGER.error("Failed to learn '%s/%s': %s", command, device, err_msg) _LOGGER.debug("Failed to enter learning mode: %s", err_msg)
raise raise
self._codes.setdefault(device, {}).update({command: code})
return True
async def _async_capture_code(self, command, timeout):
"""Enter learning mode and capture a code from a remote."""
await self.device.async_request(self.device.api.enter_learning)
self.hass.components.persistent_notification.async_create( self.hass.components.persistent_notification.async_create(
f"Press the '{command}' button.", f"Press the '{command}' button.",
title="Learn command", title="Learn command",
notification_id="learn_command", notification_id="learn_command",
) )
code = None try:
start_time = utcnow() start_time = utcnow()
while (utcnow() - start_time) < timedelta(seconds=timeout): while (utcnow() - start_time) < LEARNING_TIMEOUT:
await asyncio.sleep(1) await asyncio.sleep(1)
try: try:
code = await self.device.async_request(self.device.api.check_data) code = await self.device.async_request(self.device.api.check_data)
except (ReadError, StorageError): except (ReadError, StorageError):
continue continue
else: return b64encode(code).decode("utf8")
break raise TimeoutError("No code received")
finally:
self.hass.components.persistent_notification.async_dismiss( self.hass.components.persistent_notification.async_dismiss(
notification_id="learn_command" notification_id="learn_command"
) )
if code is None:
raise TimeoutError
return b64encode(code).decode("utf8")