text: """
  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
  """
  
  # 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
  )
  
  DOMAIN = "keyboard_remote"
  REQUIREMENTS = ['evdev==0.6.1']
  _LOGGER = logging.getLogger(__name__)
  ICON = 'mdi:remote'
  KEYBOARD_REMOTE_COMMAND_RECEIVED = 'keyboard_remote_command_received'
  KEYBOARD_REMOTE_CONNECTED = 'keyboard_remote_connected'
  KEYBOARD_REMOTE_DISCONNECTED = 'keyboard_remote_disconnected'
  KEY_CODE = 'key_code'
  KEY_VALUE = {'key_up': 0, 'key_down': 1, 'key_hold': 2}
  TYPE = 'type'
  DEVICE_DESCRIPTOR = 'device_descriptor'
  
  CONFIG_SCHEMA = vol.Schema({
      DOMAIN: vol.Schema({
          vol.Required(DEVICE_DESCRIPTOR): 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):
      """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'))
  
      keyboard_remote = KeyboardRemote(
          hass,
          device_descriptor,
          key_value
      )
  
      def _start_keyboard_remote(_event):
          keyboard_remote.run()
  
      def _stop_keyboard_remote(_event):
          keyboard_remote.stopped.set()
  
      hass.bus.listen_once(
          EVENT_HOMEASSISTANT_START,
          _start_keyboard_remote
      )
      hass.bus.listen_once(
          EVENT_HOMEASSISTANT_STOP,
          _stop_keyboard_remote
      )
  
      return True
  
  
  class KeyboardRemote(threading.Thread):
      """This interfaces with the inputdevice using evdev."""
  
      def __init__(self, hass, device_descriptor, key_value):
          """Construct a KeyboardRemote interface object."""
          from evdev import InputDevice
  
          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
          else:
              self.keyboard_connected = True
              _LOGGER.debug(
                  'KeyboardRemote: keyboard connected, %s',
                  self.dev)
  
          threading.Thread.__init__(self)
          self.stopped = threading.Event()
          self.hass = hass
          self.key_value = key_value
  
      def run(self):
          """Main loop of the KeyboardRemote."""
          from evdev import categorize, ecodes, InputDevice
  
          if self.keyboard_connected:
              self.dev.grab()
              _LOGGER.debug(
                  'KeyboardRemote 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:
                      self.dev.grab()
                      self.keyboard_connected = True
                      _LOGGER.debug('KeyboardRemote: keyboard re-connected, %s',
                                    self.device_descriptor)
                      self.hass.bus.fire(
                          KEYBOARD_REMOTE_CONNECTED
                      )
  
              try:
                  event = self.dev.read_one()
              except IOError:  # Keyboard Disconnected
                  self.keyboard_connected = False
                  _LOGGER.debug('KeyboardRemote: keyboard disconnected, %s',
                                self.device_descriptor)
                  self.hass.bus.fire(
                      KEYBOARD_REMOTE_DISCONNECTED
                  )
                  continue
  
              if not event:
                  continue
  
              # pylint: disable=no-member
              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}
                  )
  