LIRC Python Package¶
This is a python package that allows you to interact with the daemon in the Linux Infrared Remote Control package. By interacting with the daemon, it allows you to programmatically send IR signals from a computer. This package is for emitting IR signals, but it does not support listening to IR codes.
More information on the lircd daemon, socket interface, reply packet format, etc. can be found at https://www.lirc.org/html/lircd.html
Installation¶
This package is hosted on PyPI and can be installed through pip.
$ pip install lirc
However since this is a wrapper around the LIRC daemon, it is expected that LIRC is installed and setup on the given system as well.
More information on that can be found in the installation portion of the documentation.
Usage Quick Start¶
Customizing the Client¶
import lirc
client = lirc.Client()
print(client.version())
>>> '0.10.1'
To use this package, we instantiate a Client
. When we initialize
the Client
in the example above, we use the defaults by passing
it no arguments.
The defaults use the LircdConnection
class. These defaults depend
on your operating system and can be looked up in the full documentation.
However, we can customize these defaults if desired.
import socket
import lirc
client = lirc.Client(
connection=lirc.LircdConnection(
address="/var/run/lirc/lircd",
socket=socket.socket(socket.AF_UNIX, socket.SOCK_STREAM),
timeout = 5.0
)
)
The address specifies how to reach the lircd daemon. On Windows, we pass
a (hostname, port)
tuple since we connect over TCP. However on Linux and
macOS, we pass in the socket path as a string. For the client in the example
above, we set it up using the defaults for a Linux machine. While it illustrates
what is customizable, it is not a practical example since you could just call
Client()
if you’re on Linux and achieve the same outcome.
Sending IR¶
import lirc
client = lirc.Client()
client.send("my-remote-name", "KEY_POWER")
client.send("my-remote-name", "KEY_3", repeat_count=2)
With sending IR, we can use the send method and optionally, send multiple by using the repeat_count keyword argument.
Handling Errors¶
import lirc
client = lirc.Client()
try:
client.send('some-remote', 'key_power')
except lirc.LircdCommandFailureError as error:
print('The command we sent failed! Check the error message')
print(error)
If the command was not successful, a LircdCommandFailureError
exception will be thrown.
There are other errors that may be raised, which can be looked up in the full documentation,
but this is the most likely when sending commands.
Further Documentation¶
More information on how to setup the system installed LIRC, how to use this python library, and a full API specification can be found at https://lirc.readthedocs.io/
Site Documentation¶
Installing the LIRC Python & System Package¶
Since this package is merely a wrapper around the LIRC daemon, it is expected that LIRC is installed and setup on the given system as well to be able to use the python package.
System LIRC Package¶
While LIRC was originally created for Linux, there are ports of LIRC to macOS and Windows which this python package is compatibile with.
Linux:
- It is highly likely that the package manager on your system already has LIRC packaged up and ready to be installed for you. e.g.
sudo apt install lirc
on Ubuntu.- If not, you may have to compile and install it manually, but I would avoid that if possible.
Windows:
- WinLIRC at http://winlirc.sourceforge.net/ is a port for Windows. It works a bit differently since it is just a collection of files in a folder that you run. More information on that can be found at using LIRC on Windows.
macOS:
- There is a port on MacPorts at https://ports.macports.org/port/lirc/summary with it’s source code on GitHub at https://github.com/andyvand/LIRC. However, it doesn’t appear to be maintained any longer and is not the latest LIRC version. You can then run
port install lirc
or build the package from source using the instructions on the README of the GitHub repository.
Hardware Setup¶
In order to use this package, you’ll need to have an IR emitter or transciever hooked up to your computer.
Configuring System LIRC¶
Once you have your IR emitter or transciever hooked up to your computer, you’ll want to configure the system installed LIRC to ensure it works for emitting IR.
You’ll also want to ensure you have a configuration file for the remote control that you want to emulate when emitting IR since whatever you’re sending IR to will likely only understand IR codes from certain remotes.
This process will be different depending on the operating system you are using. Below are instructions for Linux, Windows, and macOS. See the LIRC configuration guide for more information. However, WinLIRC will be a bit different and you should read their own resources if you are on Windows as well.
Linux¶
LIRC configuration is typically in /etc/lirc/
. The two things you’ll have to figure
out on your own is the lirc_options.conf
file and adding your remote configuration
file as these are dependent on the hardware you use for your setup. However, I can give
general recommendations or what I typically do.
For lirc_options.conf
, the only change I make is to change the driver from
devinput
to default
. Devinput works fine for receiving IR, but it will not allow
you to emit IR. This driver is dependent on your hardware, but LIRC just works with
most devices on this driver nowadays.
For the remote configuration file, if you’re using a common remote control, you may be
able to find it in the LIRC remote control database.
Otherwise, you’ll have to create it yourself. This can be done with
LIRC’s IR record utility. However, I’ve had much
better luck using a RedRat3-II and RedRat’s
IR Signal Database for creating
the remote configuration file. The RedRat3-II is now discontinued, although
its driver’s are still available, but you
could look into the RedRatX or see if you can
find a RedRat3-II used. Place this
generated remote configuration file in your lircd.conf.d
folder.
If you’re using an Iguanaworks IR Transciever, you may find the discussion below useful. Basically, the device should just work on the default driver.
Windows¶
You’ll want to make sure you install WinLIRC at http://winlirc.sourceforge.net/. This is the LIRC port for Windows which corresponds with version 0.9.0 of LIRC. Past that, you can run the WinLIRC executable file and select the “Input Plugin” for your device. Then, you can select the remote configuration and click OK. You should now be able to select your remote and send key codes. As long as the program is running in the background (it minimizes to the tray), this package will be able to connect to it.
macOS¶
On macOS, the paths are almost the same as the Linux ones, just prefixed with /opt/local/
.
Therefore, the LIRC configuration is typically at /opt/local/etc/lirc/
and the lircd socket
is at /opt/local/var/run/lirc/lircd
Refer to the Linux section for the rest of the configuration as they are almost the same besides
the /opt/local/
prefix. However, on macOS, there is also no default
driver like there is
on Linux. You’ll have to figure out what devices will work and what driver it needs so you can
input that into lirc_options.conf
.
Example Remote Configuration File¶
The following is an example of a remote configuration
file that would be placed inside of the lircd.conf.d/
folder.
This is for a
KENMORE_253-79081,
remote taken from the LIRC remote database.
# Please make this file available to others
# by sending it to <lirc@bartelmus.de>
#
# this config file was automatically generated
# using lirc-0.9.0-pre1(default) on Sun Sep 7 00:53:46 2014
#
# contributed by Steven Shamlian
#
# brand: Kenmore
# model no. of remote control: Unknown
# devices being controlled by this remote: Kenmore 253.79081
#
# Kernel revision: 3.12.26+
# Driver/device option: --driver default --device /dev/lirc0
# Capture device: Vishay TSOP6238 to Raspberry Pi GPIO pin 23
# Kernel modules: lirc_rpi
# Type of device controlled: Air Conditioner
# Devices controlled: Kenmore 253.79081
#
# Remote Layout:
#
# /------------------------\
# |KEY_POWER KEY_TIME|
# | |
# |KEY_VOLUMEUP KEY_UP|
# | KEY_PLAY |
# |KEY_VOLUMEDOWN KEY_DOWN|
# | KEY_SAVE |
# |KEY_SHUFFLE KEY_SLEEP|
# | KEY_PAUSE |
# \------------------------/
# VOLUME keys are for fan speed
# PLAY starts air conditioner
# PAUSE makes unit fan-only
# SAVE is Energy Saver mode
# SHUFFLE is for Automatic Fan
begin remote
name KENMORE_253-79081
bits 16
flags SPACE_ENC|CONST_LENGTH
eps 30
aeps 100
header 9159 4455
one 639 1615
zero 639 486
ptrail 637
repeat 9103 2199
pre_data_bits 16
pre_data 0x10AF
gap 108066
toggle_bit_mask 0x0
begin codes
KEY_POWER 0x8877
KEY_TIME 0x609F
KEY_VOLUMEUP 0x807F
KEY_VOLUMEDOWN 0x20DF
KEY_PLAY 0x906F
KEY_UP 0x708F
KEY_DOWN 0xB04F
KEY_SAVE 0x40BF
KEY_SHUFFLE 0xF00F
KEY_SLEEP 0x00FF
KEY_PAUSE 0xE01F
end codes
end remote
Example LIRC Options Configuration File¶
This is a lirc_options.conf
file, taken
from /etc/lirc/lirc_options.conf
on a
Linux machine, to get a feel for the configuration
options offered.
# These are the default options to lircd, if installed as
# /etc/lirc/lirc_options.conf. See the lircd(8) and lircmd(8)
# manpages for info on the different options.
#
# Some tools including mode2 and irw uses values such as
# driver, device, plugindir and loglevel as fallback values
# in not defined elsewhere.
[lircd]
nodaemon = False
driver = default
device = auto
output = /var/run/lirc/lircd
pidfile = /var/run/lirc/lircd.pid
plugindir = /usr/lib/lirc/plugins
permission = 666
allow-simulate = No
repeat-max = 600
#effective-user =
#listen = [address:]port
#connect = host[:port]
#loglevel = 6
#release = true
#release_suffix = _EVUP
#logfile = ...
#driver-options = ...
[lircmd]
uinput = False
nodaemon = False
# [modinit]
# code = /usr/sbin/modprobe lirc_serial
# code1 = /usr/bin/setfacl -m g:lirc:rw /dev/uinput
# code2 = ...
# [lircd-uinput]
# add-release-events = False
# release-timeout = 200
# release-suffix = _EVUP
Usage¶
Once you’ve installed the lirc
python package, there will be a number
of things you can now import from it to get started.
from lirc import Client, LircdConnection
The most relevant of these is Client
, since this is the main class
you will be using. LircdConnection
is the object that is used to configure
the connection to LIRC when you initialize the Client
.
Initializing Lirc¶
The Client
class takes in one optional keyword argument: connection.
This connection must be a LircdConnection
. This connection, if not
specified manually, will have default values that depend on the operating
system you are on. So the simplest way to construct Client
is with no
arguments at all.
from lirc import Client
lirc_client = Client()
Overriding LIRC Defaults on Initialization¶
However, if we the defaults don’t work for us? Let’s say we’re on Windows
and we want to connect over TCP to a remote LIRC server on another Windows
machine. So we’ve passed in an address
to override the default so it doesn’t
look for the daemon on the localhost. socket
and timeout
are passed in
just to show that we can, these are already the defaults on Windows.
import socket
from lirc import Client, LircdConnection
client = Client(
connection=LircdConnection(
address=("10.16.30.2", 8765),
socket=socket.socket(socket.AF_INET, socket.SOCK_STREAM),
timeout=5.0
)
)
LIRC Initialization Defaults per Operating System¶
From the options we may pass into the LircdConnection
, address
and socket
will change depending on the operating system you are using.
The timeout
always defaults to 5.0 (seconds).
On Linux, this will attempt to connect to the lircd socket at
/var/run/lirc/lircd
and create a socket using AF_UNIX
and
SOCK_STREAM
.
On macOS, it will be almost identical to Linux except that all the paths
on macOS will be prefixed by /opt/local/
so the connection to the lircd
socket will instead be at /opt/local/var/run/lirc/lircd
. The socket that
is created will be the same.
However if we are on Windows, we can’t use unix domain sockets. Instead,
WinLIRC uses TCP to communicate with the lirc daemon. So instead of a string
for the address, it defaults to a tuple of (“localhost”, 8765), which is the
default on WinLIRC. The first part contains the address whereas the second is
the port. Furthermore, the socket that is created uses AF_INET
and
SOCK_STREAM
instead so we can connect over TCP.
API Specification¶
-
class
lirc.
Client
(connection: lirc.connection.lircd_connection.LircdConnection = <lirc.connection.lircd_connection.LircdConnection object>)¶ Bases:
object
Communicate with the lircd daemon.
-
close
()¶ Close the connection to the socket.
-
driver_option
(key: str, value: str) → None¶ Set driver-specific option named key to given value.
Raises: LircdCommandFailure
– If the command fails.
-
list_remote_keys
(remote: str) → List[str]¶ List all the keys for a specific remote.
Parameters: remote – The remote to list the keys of. Raises: LircdCommandFailure
– If the command fails.Returns: The list of keys from the remote.
-
list_remotes
() → List[str]¶ List all the remotes that lirc has in its lircd.conf.d folder.
Raises: LircdCommandFailure
– If the command fails.Returns: The list of all remotes.
-
send
(remote: str, key: str, repeat_count: int = 1) → None¶ Send an lircd SEND_ONCE command.
Parameters: - key – The name of the key to send.
- remote – The remote to use keys from.
- repeat_count – The number of times to press this key.
Raises: LircdCommandFailure
– If the command fails.
-
set_transmitters
(transmitters: Union[int, List[int]]) → None¶ Raises: LircdCommandFailure
– If the command fails.
-
simulate
(remote: str, key: str, repeat_count: int = 1, keycode: int = 0) → None¶ The –allow-simulate command line option to lircd must be active for this command not to fail.
Raises: LircdCommandFailure
– If the command fails.
-
start_logging
(path: str) → None¶ Send a lircd SET_INPUTLOG command which sets the path to log all lircd received data to.
Raises: LircdCommandFailure
– If the command fails.
-
start_repeat
(remote: str, key: str) → None¶ Send an lircd SEND_START command.
This will repeat the given key until stop_repeat() is called.
Parameters: - remote – The remote to use keys from.
- key – The name of the key to start sending.
Raises: LircdCommandFailure
– If the command fails.
-
stop_logging
() → None¶ Stop logging to the inputlog path from start_logging.
Raises: LircdCommandFailure
– If the command fails.
-
stop_repeat
(remote: str = '', key: str = '') → None¶ Send an lircd SEND_STOP command.
Parameters: - remote – The remote to stop.
- key – The key to stop sending.
- default to the remote and key (These) –
- used with send_start if not specified, (last) –
- the most likely use case is sending a (since) –
- and then a send_stop. (send_start) –
Raises: LircdCommandFailure
– If the command fails.
-
version
() → str¶ Retrieve the version of LIRC
Raises: LircdCommandFailure
– If the command fails.Returns: The version of LIRC being used.
-
-
class
lirc.
LircdConnection
(address: Union[str, tuple] = '/var/run/lirc/lircd', socket: socket.socket = <socket.socket fd=3, family=AddressFamily.AF_UNIX, type=SocketKind.SOCK_STREAM, proto=0>, timeout: float = 5.0)¶ Bases:
lirc.connection.abstract_connection.AbstractConnection
-
address
¶
-
close
()¶ Closes the socket connection.
-
connect
()¶ Connect to the socket at the address both specified on init.
Raises: LircdConnectionError
– If the address is invalid or lircd- is not running.
-
readline
() → str¶ Read a line of data from the lircd socket.
We read 4096 bytes at a time as the buffer size. Therefore after data is read from the socket, all the lines are stored in a buffer if there is more than 1 and subsequent calls grab a line that stored in that buffer until it is empty. Then, another call to the socket would be made.
Raises: TimeoutError
– If we are not able to grab data from- the socket in a specified amount of time (the initial
- timeout time on initialization).
LircdSocketError
– If some other error happened when- trying to read from the socket.
Returns: A line from the lircd socket.
-
send
(data: str)¶ Send a commend to the lircd socket connection.
Raises: TypeError
– if data is not a string.
-
socket
¶
-
Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.2.0 - 2020-12-13¶
Added
LircdConnection
to handle configuring the connection onClient
.
Changed
Lirc
is now namedClient
.Client
now takes in aconnection
as the optional argument to configure it’s connection. Thatconnection
must be aLircdConnection
class if you would like to customize the connection. TheLircdConnection
takes in anaddress
,socket
, andtimeout
with optional keyword arguments. Anything not specified with use the defaults for that operating system.
Removed
DEFAULT_SOCKET_PATH
constant onClient
. It no longer makes sense with cross-platform support.ENCODING
constant onClient
.socket_path
andsocket_timeout
on theLirc
constructor.
0.1.0 - 2020-07-13¶
- Initial Release
Contributor Covenant Code of Conduct¶
Our Pledge¶
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
Our Standards¶
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others’ private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
Our Responsibilities¶
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
Scope¶
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
Enforcement¶
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at eugenetriguba@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.
Attribution¶
This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
MIT License¶
Copyright (c) 2020 Eugene Triguba
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Contributing Guidelines¶
Thank you for your interest! If you find a bug, want to suggest an improvement, or any other change, please open a issue first. This ensures your time is not wasted if you were planning on creating a pull request, but the changes suggested do not work for this project.
General Guidelines¶
Commit history:
- Try to keep a clean commit history so it is easier to see your changes. Keep functional changes and refactorings in separate commits.
Commit messages:
- Have a short one line summary of your change followed by as many paragraphs of explanation as you need. This is the place to clarify any subtleties you have in your implementation, document other approaches you tried that didn’t end up working, any limitations on your implementation, etc. The most important part here is to describe why you made the change you did, not simply what the change you made is.
Changelog:
- Please ensure to update the changelog by adding a new bullet under an Added, Changed, Deprecated, Removed, Fixed, or Security section headers under the Unreleased version. If any of those sections are not present, feel free to add the one you need. See Keep a Changelog if you need guidance on what makes a good entry since this project follows those principles.
Tests:
- Ensure the tests pass:
poetry run task test
to run all tests.- For any significant code changes, there must be tests to accompany them. All unit tests are written with
pytest
.Code Format:
- There is a pre-commit pipeline to ensure a standard code format. Make sure to install the pre-commit hooks before making any commits with
pre-commit install
.CI Pipeline:
- There is a CI pipeline that is run using Github Actions on commits to master, dev, and on pull requests. This pipeline must pass for your changes to be accepted.
Getting Up & Running¶
This project uses Poetry for the build system and dependency management. To get started, you will want that installed on your system.
Once you’ve installed Poetry
, you can install the dependencies, this package, and go into the
virtual environment.
$ poetry install
$ poetry shell
From inside the virtual environment, we can work with the package and easily run the tasks for
this project such as task test
and task lint
that are in the pyproject.toml
file.