diff --git a/.coveragerc b/.coveragerc index b1439685ffb..c8a77217983 100644 --- a/.coveragerc +++ b/.coveragerc @@ -115,6 +115,7 @@ omit = homeassistant/components/light/lifx.py homeassistant/components/light/limitlessled.py homeassistant/components/light/osramlightify.py + homeassistant/components/lirc.py homeassistant/components/media_player/cast.py homeassistant/components/media_player/denon.py homeassistant/components/media_player/firetv.py diff --git a/homeassistant/components/lirc.py b/homeassistant/components/lirc.py new file mode 100644 index 00000000000..ec172d1b7f2 --- /dev/null +++ b/homeassistant/components/lirc.py @@ -0,0 +1,78 @@ +""" +LIRC interface to receive signals from a infrared remote control. + +This sensor will momentarily set state to various values as defined +in the .lintrc file which can be interpreted in home-assistant to +trigger various actions. + +Sending signals to other IR receivers can be accomplished with the +shell_command component and the irsend command for now. +""" +# pylint: disable=import-error +import threading +import time +import logging + +from homeassistant.const import (EVENT_HOMEASSISTANT_STOP, + EVENT_HOMEASSISTANT_START) + +DOMAIN = "lirc" +REQUIREMENTS = ['python-lirc==1.2.1'] +_LOGGER = logging.getLogger(__name__) +ICON = 'mdi:remote' +EVENT_IR_COMMAND_RECEIVED = 'ir_command_received' +BUTTON_NAME = 'button_name' + + +def setup(hass, config): + """Setup LIRC capability.""" + import lirc + + # blocking=True gives unexpected behavior (multiple responses for 1 press) + # also by not blocking, we allow hass to shut down the thread gracefully + # on exit. + lirc.init('home-assistant', blocking=False) + lirc_interface = LircInterface(hass) + + def _start_lirc(_event): + lirc_interface.start() + + def _stop_lirc(_event): + lirc_interface.stopped.set() + + hass.bus.listen_once(EVENT_HOMEASSISTANT_START, _start_lirc) + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, _stop_lirc) + + return True + + +class LircInterface(threading.Thread): + """ + This interfaces with the lirc daemon to read IR commands. + + When using lirc in blocking mode, sometimes repeated commands get produced + in the next read of a command so we use a thread here to just wait + around until a non-empty response is obtained from lirc. + """ + + def __init__(self, hass): + """Construct a LIRC interface object.""" + threading.Thread.__init__(self) + self.daemon = True + self.stopped = threading.Event() + self.hass = hass + + def run(self): + """Main loop of LIRC interface thread.""" + import lirc + while not self.stopped.isSet(): + code = lirc.nextcode() # list; empty if no buttons pressed + # interpret result from python-lirc + if code: + code = code[0] + _LOGGER.info('Got new LIRC code %s', code) + self.hass.bus.fire(EVENT_IR_COMMAND_RECEIVED, + {BUTTON_NAME: code}) + else: + time.sleep(0.2) + _LOGGER.info('LIRC interface thread stopped') diff --git a/requirements_all.txt b/requirements_all.txt index 01b30bda357..5439177dba3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -267,6 +267,9 @@ pysnmp==4.2.5 # homeassistant.components.sensor.forecast python-forecastio==1.3.4 +# homeassistant.components.lirc +# python-lirc==1.2.1 + # homeassistant.components.media_player.mpd python-mpd2==0.5.5 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 76ed3acba39..872d13bab75 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -13,6 +13,7 @@ COMMENT_REQUIREMENTS = [ 'fritzconnection', 'pybluez', 'bluepy', + 'python-lirc', ]