From 4af6f124fbd5d8fd946d556ec278ccc21278fc23 Mon Sep 17 00:00:00 2001 From: Bryan Jacobs Date: Sun, 10 Dec 2017 22:16:21 +1100 Subject: [PATCH 1/3] Allow using more than one keyboard remote This sets up one thread per keyboard remote, listening for events. --- homeassistant/components/keyboard_remote.py | 67 +++++++++++++++++---- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/keyboard_remote.py b/homeassistant/components/keyboard_remote.py index 5a81f6d2a9e3c3..ee4bd2b21f8328 100644 --- a/homeassistant/components/keyboard_remote.py +++ b/homeassistant/components/keyboard_remote.py @@ -21,8 +21,9 @@ _LOGGER = logging.getLogger(__name__) DEVICE_DESCRIPTOR = 'device_descriptor' -DEVICE_ID_GROUP = 'Device descriptor or name' +DEVICE_ID_GROUP = 'Device description' DEVICE_NAME = 'device_name' +DEVICES = 'devices' DOMAIN = 'keyboard_remote' ICON = 'mdi:remote' @@ -40,7 +41,15 @@ vol.Exclusive(DEVICE_DESCRIPTOR, DEVICE_ID_GROUP): cv.string, vol.Exclusive(DEVICE_NAME, DEVICE_ID_GROUP): cv.string, vol.Optional(TYPE, default='key_up'): - vol.All(cv.string, vol.Any('key_up', 'key_down', 'key_hold')), + vol.All(cv.string, vol.Any('key_up', 'key_down', 'key_hold')), + vol.Exclusive(DEVICES, DEVICE_ID_GROUP): vol.All(cv.ensure_list, [ + vol.Schema({ + vol.Exclusive(DEVICE_DESCRIPTOR, DEVICE_ID_GROUP): cv.string, + vol.Exclusive(DEVICE_NAME, DEVICE_ID_GROUP): cv.string, + vol.Optional(TYPE, default='key_up'): + vol.All(cv.string, vol.Any('key_up', 'key_down', 'key_hold')) + }) + ]), }), }, extra=vol.ALLOW_EXTRA) @@ -50,8 +59,9 @@ def setup(hass, config): config = config.get(DOMAIN) if not config.get(DEVICE_DESCRIPTOR) and\ - not config.get(DEVICE_NAME): - _LOGGER.error("No device_descriptor or device_name found") + not config.get(DEVICE_NAME) and\ + not config.get(DEVICES): + _LOGGER.error("No device_descriptor, device_name, or dev block found") return keyboard_remote = KeyboardRemote( @@ -63,7 +73,7 @@ def _start_keyboard_remote(_event): keyboard_remote.run() def _stop_keyboard_remote(_event): - keyboard_remote.stopped.set() + keyboard_remote.stop() hass.bus.listen_once( EVENT_HOMEASSISTANT_START, @@ -77,19 +87,21 @@ def _stop_keyboard_remote(_event): return True -class KeyboardRemote(threading.Thread): +class KeyboardRemoteThread(threading.Thread): """This interfaces with the inputdevice using evdev.""" - def __init__(self, hass, config): - """Construct a KeyboardRemote interface object.""" - from evdev import InputDevice, list_devices + def __init__(self, hass, device_name, device_descriptor, key_value): + """Construct a thread listening for events on one device.""" + self.hass = hass + self.device_name = device_name + self.device_descriptor = device_descriptor + self.key_value = key_value - self.device_descriptor = config.get(DEVICE_DESCRIPTOR) - self.device_name = config.get(DEVICE_NAME) if self.device_descriptor: self.device_id = self.device_descriptor else: self.device_id = self.device_name + self.dev = self._get_keyboard_device() if self.dev is not None: _LOGGER.debug("Keyboard connected, %s", self.device_id) @@ -103,6 +115,7 @@ def __init__(self, hass, config): id_folder = '/dev/input/by-id/' if os.path.isdir(id_folder): + from evdev import InputDevice, list_devices device_names = [InputDevice(file_name).name for file_name in list_devices()] _LOGGER.debug( @@ -116,7 +129,6 @@ def __init__(self, hass, config): threading.Thread.__init__(self) self.stopped = threading.Event() self.hass = hass - self.key_value = KEY_VALUE.get(config.get(TYPE, 'key_up')) def _get_keyboard_device(self): """Get the keyboard device.""" @@ -145,7 +157,7 @@ def run(self): while not self.stopped.isSet(): # Sleeps to ease load on processor - time.sleep(.1) + time.sleep(.05) if self.dev is None: self.dev = self._get_keyboard_device() @@ -178,3 +190,32 @@ def run(self): KEYBOARD_REMOTE_COMMAND_RECEIVED, {KEY_CODE: event.code} ) + + +class KeyboardRemote(object): + """Sets up one thread per device.""" + + def __init__(self, hass, config): + """Construct a KeyboardRemote interface object.""" + self.threads = [] + for dev_block in config.get(DEVICES, []) + [config]: + device_descriptor = dev_block.get(DEVICE_DESCRIPTOR) + device_name = dev_block.get(DEVICE_NAME) + key_value = KEY_VALUE.get(dev_block.get(TYPE, 'key_up')) + + if device_descriptor is not None\ + or device_name is not None: + thread = KeyboardRemoteThread(hass, device_name, + device_descriptor, + key_value) + self.threads.append(thread) + + def run(self): + """Run all event listener threads.""" + for thread in self.threads: + thread.start() + + def stop(self): + """Stop all event listener threads.""" + for thread in self.threads: + thread.stopped.set() From 1d6bef48a6f851995fb939eb1bb57931e5807c90 Mon Sep 17 00:00:00 2001 From: Bryan Jacobs Date: Mon, 11 Dec 2017 19:17:04 +1100 Subject: [PATCH 2/3] Remove enclosing block in keyboard_remote --- homeassistant/components/keyboard_remote.py | 22 +++++++-------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/keyboard_remote.py b/homeassistant/components/keyboard_remote.py index ee4bd2b21f8328..53d69492f02128 100644 --- a/homeassistant/components/keyboard_remote.py +++ b/homeassistant/components/keyboard_remote.py @@ -23,7 +23,6 @@ DEVICE_DESCRIPTOR = 'device_descriptor' DEVICE_ID_GROUP = 'Device description' DEVICE_NAME = 'device_name' -DEVICES = 'devices' DOMAIN = 'keyboard_remote' ICON = 'mdi:remote' @@ -37,20 +36,13 @@ TYPE = 'type' CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - vol.Exclusive(DEVICE_DESCRIPTOR, DEVICE_ID_GROUP): cv.string, - vol.Exclusive(DEVICE_NAME, DEVICE_ID_GROUP): cv.string, - vol.Optional(TYPE, default='key_up'): - vol.All(cv.string, vol.Any('key_up', 'key_down', 'key_hold')), - vol.Exclusive(DEVICES, DEVICE_ID_GROUP): vol.All(cv.ensure_list, [ - vol.Schema({ - vol.Exclusive(DEVICE_DESCRIPTOR, DEVICE_ID_GROUP): cv.string, - vol.Exclusive(DEVICE_NAME, DEVICE_ID_GROUP): cv.string, - vol.Optional(TYPE, default='key_up'): + DOMAIN: + vol.All(cv.ensure_list, [vol.Schema({ + vol.Exclusive(DEVICE_DESCRIPTOR, DEVICE_ID_GROUP): cv.string, + vol.Exclusive(DEVICE_NAME, DEVICE_ID_GROUP): cv.string, + vol.Optional(TYPE, default='key_up'): vol.All(cv.string, vol.Any('key_up', 'key_down', 'key_hold')) - }) - ]), - }), + })]) }, extra=vol.ALLOW_EXTRA) @@ -198,7 +190,7 @@ class KeyboardRemote(object): def __init__(self, hass, config): """Construct a KeyboardRemote interface object.""" self.threads = [] - for dev_block in config.get(DEVICES, []) + [config]: + for dev_block in config: device_descriptor = dev_block.get(DEVICE_DESCRIPTOR) device_name = dev_block.get(DEVICE_NAME) key_value = KEY_VALUE.get(dev_block.get(TYPE, 'key_up')) From 8ff87884dc64a4ed33aac954539c5835436a4a6c Mon Sep 17 00:00:00 2001 From: Bryan Jacobs Date: Mon, 11 Dec 2017 19:22:55 +1100 Subject: [PATCH 3/3] Remove unnecessary semantic check for keyboard_remote --- homeassistant/components/keyboard_remote.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/homeassistant/components/keyboard_remote.py b/homeassistant/components/keyboard_remote.py index 53d69492f02128..d737c555873b9f 100644 --- a/homeassistant/components/keyboard_remote.py +++ b/homeassistant/components/keyboard_remote.py @@ -50,12 +50,6 @@ def setup(hass, config): """Set up the keyboard_remote.""" config = config.get(DOMAIN) - if not config.get(DEVICE_DESCRIPTOR) and\ - not config.get(DEVICE_NAME) and\ - not config.get(DEVICES): - _LOGGER.error("No device_descriptor, device_name, or dev block found") - return - keyboard_remote = KeyboardRemote( hass, config