core/homeassistant/components/keyboard_remote.py

192 lines
6.2 KiB
Python
Raw Normal View History

"""
Receive signals from a keyboard and use it as a remote control.
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/keyboard_remote/
"""
# pylint: disable=import-error
import threading
import logging
import os
import time
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
REQUIREMENTS = ['evdev==0.6.1']
_LOGGER = logging.getLogger(__name__)
DEVICE_DESCRIPTOR = 'device_descriptor'
DEVICE_ID_GROUP = 'Device description'
DEVICE_NAME = 'device_name'
DOMAIN = 'keyboard_remote'
ICON = 'mdi:remote'
KEY_CODE = 'key_code'
KEY_VALUE = {'key_up': 0, 'key_down': 1, 'key_hold': 2}
KEYBOARD_REMOTE_COMMAND_RECEIVED = 'keyboard_remote_command_received'
KEYBOARD_REMOTE_CONNECTED = 'keyboard_remote_connected'
KEYBOARD_REMOTE_DISCONNECTED = 'keyboard_remote_disconnected'
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
TYPE = 'type'
CONFIG_SCHEMA = vol.Schema({
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)
def setup(hass, config):
"""Set up the keyboard_remote."""
config = config.get(DOMAIN)
keyboard_remote = KeyboardRemote(hass, config)
def _start_keyboard_remote(_event):
keyboard_remote.run()
def _stop_keyboard_remote(_event):
keyboard_remote.stop()
hass.bus.listen_once(EVENT_HOMEASSISTANT_START, _start_keyboard_remote)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _stop_keyboard_remote)
return True
class KeyboardRemoteThread(threading.Thread):
"""This interfaces with the inputdevice using evdev."""
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
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
if self.device_descriptor:
self.device_id = self.device_descriptor
else:
self.device_id = self.device_name
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
self.dev = self._get_keyboard_device()
if self.dev is not None:
_LOGGER.debug("Keyboard connected, %s", self.device_id)
else:
_LOGGER.debug(
"Keyboard not connected, %s. "
"Check /dev/input/event* permissions", self.device_id)
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(
"Possible device names are: %s. "
"Possible device descriptors are %s: %s",
device_names, id_folder, os.listdir(id_folder))
threading.Thread.__init__(self)
self.stopped = threading.Event()
self.hass = hass
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
def _get_keyboard_device(self):
"""Get the keyboard device."""
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
from evdev import InputDevice, list_devices
if self.device_name:
devices = [InputDevice(file_name) for file_name in list_devices()]
for device in devices:
if self.device_name == device.name:
return device
elif self.device_descriptor:
try:
device = InputDevice(self.device_descriptor)
except OSError:
pass
else:
return device
return None
def run(self):
"""Run the loop of the KeyboardRemote."""
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
from evdev import categorize, ecodes
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
if self.dev is not None:
self.dev.grab()
_LOGGER.debug("Interface started for %s", self.dev)
while not self.stopped.isSet():
# Sleeps to ease load on processor
time.sleep(.05)
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
if self.dev is None:
self.dev = self._get_keyboard_device()
if self.dev is not None:
self.dev.grab()
self.hass.bus.fire(KEYBOARD_REMOTE_CONNECTED)
_LOGGER.debug("Keyboard re-connected, %s", self.device_id)
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
else:
continue
try:
event = self.dev.read_one()
except IOError: # Keyboard Disconnected
Update keyboard_remote.py (#5535) * Update keyboard_remote.py I added a couple of events: keyboard_remote_connected and keyboard_remote_disconnected, useful to trigger some action (for example: play a sound) I changed the way the component refers to the keyboard: not by "descriptor", but by name. The fact is that the udev system doesn't always give a name link in /dev/input/by-id folder and the actual /dev/input/eventX file changes automatically. For example, if I had my keyboard on /dev/input/event13 and then it disconnected, if something else connects to the system it will get the first input file available, that is /dev/input/event13. If the keyboard then reconnects, it will get /dev/input/event14, thus breaking the configuration. Now it searches every time for the right input file. The problem might be that, in case of ha upgrade, the pre-existing configuration won't work. I thing there are some guidelines here, but I am not sure. I think it's inevitable: the initial idea to use a /dev/input/by-id/ symbolic link was good, but unfortunately it doesn't seem to work with bluetooth devices. I haven't updated the documentation yet: I'm waiting for some ok about the change in configuration key names. * Update keyboard_remote.py That should do the trick. You are right: device names can be duplicated, thus they're not reliable in case of multiple devices. Unfortunately, the only other information available is the physical address, even more complicated. But in case you have just one keyboard remote of same model, the name works just fine. I'll put it in the documentation which, once the code is approved, I will promptly update. * Update keyboard_remote.py * Update keyboard_remote.py * Update keyboard_remote.py * Unwrap logger error
2017-01-25 18:58:34 +00:00
self.dev = None
self.hass.bus.fire(KEYBOARD_REMOTE_DISCONNECTED)
_LOGGER.debug("Keyboard disconnected, %s", self.device_id)
continue
if not event:
continue
if event.type is ecodes.EV_KEY and event.value is self.key_value:
_LOGGER.debug(categorize(event))
self.hass.bus.fire(
KEYBOARD_REMOTE_COMMAND_RECEIVED,
{
KEY_CODE: event.code,
DEVICE_DESCRIPTOR: self.device_descriptor,
DEVICE_NAME: self.device_name
}
)
class KeyboardRemote:
"""Sets up one thread per device."""
def __init__(self, hass, config):
"""Construct a KeyboardRemote interface object."""
self.threads = []
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'))
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()