This repository was archived by the owner on Jul 18, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlircd_connection.py
More file actions
148 lines (119 loc) · 4.84 KB
/
lircd_connection.py
File metadata and controls
148 lines (119 loc) · 4.84 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
import socket
from collections import deque
from typing import Union
from lirc.exceptions import LircdConnectionError, LircdSocketError
from .abstract_connection import AbstractConnection
from .default_connection import DefaultConnection
class LircdConnection(AbstractConnection):
def __init__(
self,
address: Union[str, tuple] = None,
socket: socket.socket = None,
timeout: float = 5.0,
):
"""Initialize the LircdConnection. This sets up state we'll
need, but it does not connect to that socket. To connect,
we can call connect() after initialization.
Args:
address: The address to the socket. Defaults to different
values depending on the host operating system. On Linux,
it defaults to ``/var/run/lirc/lircd``. On Windows, a tuple
of ``("localhost", 8765)``. And on Darwin (macOS),
``/opt/local/var/run/lirc/lircd``.
socket: The socket to use to connect to lircd. The default
socket is determined using the host operating system. For
Linux and Darwin, a unix domain socket connection is used
i.e. ``socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)``.
However on Windows, a TCP socket is used
i.e. ``socket.socket(socket.AF_INET, socket.SOCK_STREAM)``.
timeout: The amount of time to wait for data from the socket before
we timeout.
"""
default = DefaultConnection()
if address is None:
address = default.address
if socket is None:
socket = default.socket
self._buffer = deque()
self._buffer_size = 4096
self._address = address
self._socket = socket
self._socket.settimeout(timeout)
def connect(self):
"""Connect to the socket at the address both specified on init.
Raises:
LircdConnectionError: If the address is invalid or lircd
is not running.
"""
try:
self._socket.connect(self._address)
except FileNotFoundError:
raise LircdConnectionError(
f"Could not connect to lircd at {self._address} with socket "
f"{self._socket}. Did you start the `lircd` daemon?"
)
except Exception as error:
raise LircdConnectionError(error)
@property
def address(self) -> str:
"""Retrieve the address that this lircd connection
is connected to.
Returns:
The current address being used.
"""
return self._address
def close(self):
"""Closes the socket connection.
"""
self._socket.close()
def send(self, data: str):
"""Send a commend to the lircd socket connection.
Args:
data: The data to send to the lircd socket.
Raises:
TypeError: if data is not a string.
"""
if not isinstance(data, str):
raise TypeError("data parameter to send() must be a string")
if not data.endswith("\n"):
data += "\n"
self._socket.sendall(data.encode("utf-8"))
def readline(self) -> 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.
"""
if len(self._buffer) >= 1:
return self._buffer.popleft()
try:
packet = self._socket.recv(self._buffer_size)
if packet.endswith(b"\n"):
packet = packet.strip()
if b"\n" in packet:
self._buffer.extend(
line.decode("utf-8") for line in packet.split(b"\n")
)
else:
self._buffer.append(packet.decode("utf-8"))
return self._buffer.popleft()
except socket.timeout:
raise TimeoutError(
"could not find any data on the socket after "
f"{self._socket.gettimeout()} seconds, socket timed out."
)
except OSError as error:
raise LircdSocketError(
f"An error occurred while reading from the lircd socket: {error}"
)