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
pull/5555/head
Gianluca Barbaro 2017-01-25 19:58:34 +01:00 committed by Adam Mills
parent c3a55e7d82
commit 9cad9c19f8
1 changed files with 68 additions and 68 deletions

View File

@ -1,32 +1,8 @@
"""
Receive signals from a keyboard and use it as a remote control.
This component allows to use a keyboard as remote control. It will
fire ´keyboard_remote_command_received´ events witch can then be used
in automation rules.
The `evdev` package is used to interface with the keyboard and thus this
is Linux only. It also means you can't use your normal keyboard for this,
because `evdev` will block it.
Example:
keyboard_remote:
device_descriptor: '/dev/input/by-id/foo'
type: 'key_up' # optional alternaive 'key_down' and 'key_hold'
# be carefull, 'key_hold' fires a lot of events
and an automation rule to bring breath live into it.
automation:
alias: Keyboard All light on
trigger:
platform: event
event_type: keyboard_remote_command_received
event_data:
key_code: 107 # inspect log to obtain desired keycode
action:
service: light.turn_on
entity_id: light.all
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/keyboard_remote/
"""
# pylint: disable=import-error
@ -54,10 +30,14 @@ KEY_CODE = 'key_code'
KEY_VALUE = {'key_up': 0, 'key_down': 1, 'key_hold': 2}
TYPE = 'type'
DEVICE_DESCRIPTOR = 'device_descriptor'
DEVICE_NAME = 'device_name'
DEVICE_ID_GROUP = 'Device descriptor or name'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(DEVICE_DESCRIPTOR): cv.string,
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')),
}),
@ -67,22 +47,15 @@ CONFIG_SCHEMA = vol.Schema({
def setup(hass, config):
"""Setup keyboard_remote."""
config = config.get(DOMAIN)
device_descriptor = config.get(DEVICE_DESCRIPTOR)
if not device_descriptor:
id_folder = '/dev/input/'
_LOGGER.error(
'A device_descriptor must be defined. '
'Possible descriptors are %s:\n%s',
id_folder, os.listdir(id_folder)
)
return
key_value = KEY_VALUE.get(config.get(TYPE, 'key_up'))
if not config.get(DEVICE_DESCRIPTOR) and\
not config.get(DEVICE_NAME):
_LOGGER.error('No device_descriptor or device_name found.')
return
keyboard_remote = KeyboardRemote(
hass,
device_descriptor,
key_value
config
)
def _start_keyboard_remote(_event):
@ -106,66 +79,93 @@ def setup(hass, config):
class KeyboardRemote(threading.Thread):
"""This interfaces with the inputdevice using evdev."""
def __init__(self, hass, device_descriptor, key_value):
def __init__(self, hass, config):
"""Construct a KeyboardRemote interface object."""
from evdev import InputDevice
from evdev import InputDevice, list_devices
self.device_descriptor = device_descriptor
try:
self.dev = InputDevice(device_descriptor)
except OSError: # Keyboard not present
_LOGGER.debug(
'KeyboardRemote: keyboard not connected, %s',
self.device_descriptor)
self.keyboard_connected = False
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.keyboard_connected = True
self.device_id = self.device_name
self.dev = self._get_keyboard_device()
if self.dev is not None:
_LOGGER.debug(
'KeyboardRemote: keyboard connected, %s',
self.dev)
'Keyboard connected, %s',
self.device_id
)
else:
id_folder = '/dev/input/by-id/'
device_names = [InputDevice(file_name).name
for file_name in list_devices()]
_LOGGER.debug(
'Keyboard not connected, %s.\n\
Check /dev/input/event* permissions.\
Possible device names are:\n %s.\n \
Possible device descriptors are %s:\n %s',
self.device_id,
device_names,
id_folder,
os.listdir(id_folder)
)
threading.Thread.__init__(self)
self.stopped = threading.Event()
self.hass = hass
self.key_value = key_value
self.key_value = KEY_VALUE.get(config.get(TYPE, 'key_up'))
def _get_keyboard_device(self):
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):
"""Main loop of the KeyboardRemote."""
from evdev import categorize, ecodes, InputDevice
from evdev import categorize, ecodes
if self.keyboard_connected:
if self.dev is not None:
self.dev.grab()
_LOGGER.debug(
'KeyboardRemote interface started for %s',
'Interface started for %s',
self.dev)
while not self.stopped.isSet():
# Sleeps to ease load on processor
time.sleep(.1)
if not self.keyboard_connected:
try:
self.dev = InputDevice(self.device_descriptor)
except OSError: # still disconnected
continue
else:
if self.dev is None:
self.dev = self._get_keyboard_device()
if self.dev is not None:
self.dev.grab()
self.keyboard_connected = True
_LOGGER.debug('KeyboardRemote: keyboard re-connected, %s',
self.device_descriptor)
self.hass.bus.fire(
KEYBOARD_REMOTE_CONNECTED
)
_LOGGER.debug('Keyboard re-connected, %s',
self.device_id)
else:
continue
try:
event = self.dev.read_one()
except IOError: # Keyboard Disconnected
self.keyboard_connected = False
_LOGGER.debug('KeyboardRemote: keyboard disconnected, %s',
self.device_descriptor)
self.dev = None
self.hass.bus.fire(
KEYBOARD_REMOTE_DISCONNECTED
)
_LOGGER.debug('Keyboard disconnected, %s',
self.device_id)
continue
if not event: