This repository was archived by the owner on Nov 19, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathlirc.pyx
More file actions
259 lines (204 loc) · 7.86 KB
/
lirc.pyx
File metadata and controls
259 lines (204 loc) · 7.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
lirc.pyx
Provides a Python API for the lirc libraries
Copyright (C) 2013 [email protected]
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from libc.stdlib cimport calloc, free
from posix cimport fcntl, unistd
cimport lirc_client
ENCODING = 'utf-8'
STRING_BUFFER_LEN = 256
LOCAL_CONFIG_FILE = "~/.lircrc"
GLOBAL_CONFIG_FILE = "/etc/lirc/lircrc"
initialised = False
config = None
cdef extern from "lirc/include/media/lirc.h":
ctypedef int lirc_t
ctypedef unsigned int __u32
unsigned long _LIRC_GET_SEND_MODE "LIRC_GET_SEND_MODE"
unsigned long _LIRC_GET_REC_MODE "LIRC_GET_REC_MODE"
unsigned long _LIRC_GET_REC_RESOLUTION "LIRC_GET_REC_RESOLUTION"
unsigned long _LIRC_GET_MIN_TIMEOUT "LIRC_GET_MIN_TIMEOUT"
unsigned long _LIRC_GET_MAX_TIMEOUT "LIRC_GET_MAX_TIMEOUT"
unsigned long _LIRC_GET_LENGTH "LIRC_GET_LENGTH"
unsigned long _LIRC_SET_SEND_CARRIER "LIRC_SET_SEND_CARRIER"
unsigned long _LIRC_SET_REC_CARRIER "LIRC_SET_REC_CARRIER"
unsigned long _LIRC_SET_SEND_DUTY_CYCLE "LIRC_SET_SEND_DUTY_CYCLE"
unsigned long _LIRC_SET_TRANSMITTER_MASK "LIRC_SET_TRANSMITTER_MASK"
unsigned long _LIRC_SET_REC_TIMEOUT_REPORTS "LIRC_SET_REC_TIMEOUT_REPORTS"
unsigned long _LIRC_SET_MEASURE_CARRIER_MODE "LIRC_SET_MEASURE_CARRIER_MODE"
unsigned long _LIRC_SET_REC_CARRIER_RANGE "LIRC_SET_REC_CARRIER_RANGE"
unsigned long _LIRC_SET_WIDEBAND_RECEIVER "LIRC_SET_WIDEBAND_RECEIVER"
__u32 _LIRC_MODE_MODE2 "LIRC_MODE_MODE2"
lirc_t _PULSE_BIT "PULSE_BIT"
lirc_t _PULSE_MASK "PULSE_MASK"
LIRC_T_FORMAT = "i"
LIRC_GET_SEND_MODE = _LIRC_GET_SEND_MODE
LIRC_GET_REC_MODE = _LIRC_GET_REC_MODE
LIRC_GET_REC_RESOLUTION = _LIRC_GET_REC_RESOLUTION
LIRC_GET_MIN_TIMEOUT = _LIRC_GET_MIN_TIMEOUT
LIRC_GET_MAX_TIMEOUT = _LIRC_GET_MAX_TIMEOUT
LIRC_GET_LENGTH = _LIRC_GET_LENGTH
LIRC_SET_SEND_CARRIER = _LIRC_SET_SEND_CARRIER
LIRC_SET_REC_CARRIER = _LIRC_SET_REC_CARRIER
LIRC_SET_SEND_DUTY_CYCLE = _LIRC_SET_SEND_DUTY_CYCLE
LIRC_SET_TRANSMITTER_MASK = _LIRC_SET_TRANSMITTER_MASK
LIRC_SET_REC_TIMEOUT_REPORTS = _LIRC_SET_REC_TIMEOUT_REPORTS
LIRC_SET_MEASURE_CARRIER_MODE = _LIRC_SET_MEASURE_CARRIER_MODE
LIRC_SET_REC_CARRIER_RANGE = _LIRC_SET_REC_CARRIER_RANGE
LIRC_SET_WIDEBAND_RECEIVER = _LIRC_SET_WIDEBAND_RECEIVER
LIRC_MODE_MODE2 = _LIRC_MODE_MODE2
PULSE_BIT = _PULSE_BIT
PULSE_MASK = _PULSE_MASK
class InitError(Exception):
pass
class DeinitError(Exception):
pass
class ConfigLoadError(Exception):
pass
class Code2CharError(Exception):
pass
class NextCodeError(Exception):
pass
class NoMoreStrings(Exception):
pass
cdef class LircConfig:
cdef lirc_client.lirc_config * _c_lirc_config
def __cinit__(self, config_filename):
self.add_config_file(config_filename)
def __dealloc__(self):
if self._c_lirc_config is not NULL:
lirc_client.lirc_freeconfig(self._c_lirc_config)
def add_config_file(self, config_filename):
if config_filename is not None:
lirc_client.lirc_readconfig(
config_filename, &self._c_lirc_config, NULL)
else:
lirc_client.lirc_readconfig(
NULL, &self._c_lirc_config, NULL)
if self._c_lirc_config is NULL:
raise ConfigLoadError(
"Could not load the config file (%s)" % config_filename)
def code2char(self, char * code):
"""Returns the (byte) string associated with the code in the
config file
"""
self.is_init_or_error()
cdef char * string_buf = \
<char * >calloc(STRING_BUFFER_LEN, sizeof(char))
cdef char * string_buf_2 = string_buf # string_buf might be destroyed
status = lirc_client.lirc_code2char(
self._c_lirc_config, code, &string_buf)
if status == -1:
free(string_buf)
raise Code2CharError(
"There was an error determining the config string.")
if string_buf == NULL:
free(string_buf_2)
raise NoMoreStrings()
else:
string = string_buf.decode(ENCODING)
free(string_buf_2)
return string
def is_init_or_error(self):
"""Throws an error if not initialised"""
if self._c_lirc_config is NULL:
raise InitError("LircConfig has not been inititalised.")
def init(program_name, config_filename=None, blocking=True, verbose=False):
global initialised
if initialised:
return
# init lirc
b_program_name = program_name.encode(ENCODING)
lirc_socket = lirc_client.lirc_init(b_program_name, 1 if verbose else 0)
if lirc_socket == -1:
raise InitError(
"Unable to initialise lirc (socket was -1 from C library).")
set_blocking(blocking, lirc_socket)
initialised = True
if config_filename is not None:
load_config_file(config_filename)
else:
try:
load_default_config()
except ConfigLoadError as e:
raise InitError("Unable to load default config {} or {}.".format(
LOCAL_CONFIG_FILE, GLOBAL_CONFIG_FILE)) from e
return lirc_socket
def deinit():
global initialised
if not initialised:
return
if lirc_client.lirc_deinit() == -1:
raise DeinitError("Unable to de-initialise lirc.")
config = None
initialised = False
def load_default_config():
"""Attempts to load the default lirc config files."""
try:
load_config_file(LOCAL_CONFIG_FILE)
except ConfigLoadError as local_conf_error:
try:
load_config_file(GLOBAL_CONFIG_FILE)
except ConfigLoadError as global_conf_error:
raise global_conf_error from local_conf_error
def load_config_file(config_filename=None):
"""Adds a configuration file for this instance of lirc."""
_is_init_or_error()
# read config
if config_filename:
b_config_filename = config_filename.encode(ENCODING)
else:
b_config_filename = None
global config
if config:
config.add_config_file(b_config_filename)
else:
config = LircConfig(b_config_filename)
def nextcode():
"""Returns the list of codes in the lirc queue.
May block, depending on initialisation parameters
"""
_is_init_or_error()
cdef char * code
cdef int return_value
with nogil:
return_value = lirc_client.lirc_nextcode(&code)
if return_value == -1:
free(code)
raise NextCodeError("There was an error reading the next code.")
if code == NULL:
# raise NextCodeError("There was no complete code available.")
return list()
# get all of the strings associated with this code
strings = list()
while True:
global config
try:
strings.append(config.code2char(code))
except NoMoreStrings:
break
free(code)
return strings
def set_blocking(blocking, lirc_socket):
"""Sets whether the nextcode function blocks"""
fcntl.fcntl(lirc_socket, fcntl.F_SETOWN, unistd.getpid())
flags = fcntl.fcntl(lirc_socket, fcntl.F_GETFL, 0)
flags = (flags & ~fcntl.O_NONBLOCK) | (0 if blocking else fcntl.O_NONBLOCK)
fcntl.fcntl(lirc_socket, fcntl.F_SETFL, flags)
def _is_init_or_error():
global initialised
if not initialised:
raise InitError("%s has not been initialised." % __name__)