-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathevmqtt_legacy.py
More file actions
executable file
·223 lines (172 loc) · 6.1 KB
/
evmqtt_legacy.py
File metadata and controls
executable file
·223 lines (172 loc) · 6.1 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
#!/usr/bin/env python3
"""
Linux input event to MQTT gateway
https://github.com/odtgit/evmqtt
"""
import os
import signal
import threading
import sys
import datetime
import json
from time import time
from platform import node as hostname
from pathlib import Path
import evdev
import paho.mqtt.client as mqtt
def log(s):
sys.stderr.write("[%s] %s\n" % (datetime.datetime.now(), s))
sys.stderr.flush()
class Watcher:
def __init__(self):
self.child = os.fork()
if self.child == 0:
return
else:
self.watch()
def watch(self):
try:
os.wait()
except KeyboardInterrupt:
# I put the capital B in KeyBoardInterrupt so I can
# tell when the Watcher gets the SIGINT
log('KeyBoardInterrupt received')
self.kill()
sys.exit()
def kill(self):
try:
os.kill(self.child, signal.SIGKILL)
except OSError:
pass
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
log("Connected with result code " + str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("topic")
def on_disconnect(client, userdata, rc):
log("Disconnected with result code " + str(rc))
# The callback for when a PUBLISH message is received from the server.
def on_message(msg):
msgpayload = str(msg.payload)
print(msg.topic + " " + msgpayload)
class MQTTClient(threading.Thread):
def __init__(self, clientid, mqttcfg):
super(MQTTClient, self).__init__()
serverip = mqttcfg["serverip"]
port = mqttcfg["port"]
username = mqttcfg["username"]
password = mqttcfg["password"]
log("MQTT connecting to %s:%u" % (serverip, port))
self.mqttclient = mqtt.Client(clientid, protocol=mqtt.MQTTv31)
self.mqttclient.username_pw_set(username, password)
self.mqttclient.on_connect = on_connect
self.mqttclient.on_disconnect = on_disconnect
self.mqttclient.on_message = on_message
self.mqttclient.connect(serverip, port)
self.mqttclient.loop_start()
key_state = {}
def get_modifiers():
global key_state
ret = []
for x in key_state.keys():
if key_state[x] == 1:
ret.append(x)
ret.sort()
if len(ret) == 0:
return ""
return "_" + "_".join(ret)
# the number keys on the remote always set and unset numlock - this is
# superfluous for my use-case
modifiers = [
"KEY_LEFTSHIFT",
"KEY_RIGHTSHIFT",
"KEY_LEFTCTRL",
"KEY_RIGHTCTRL"]
ignore = ["KEY_NUMLOCK"]
def set_modifier(keycode, keystate):
global key_state, modifiers
if keycode in modifiers:
key_state[keycode] = keystate
def is_modifier(keycode):
global modifiers
if keycode in modifiers:
return True
return False
def is_ignore(keycode):
global ignore
if keycode in ignore:
return True
return False
def concat_multikeys(keycode):
# Handles case on my remote where multiple keys returned,
# ie: mute returns KEY_MIN_INTERESTING and KEY_MUTE in a list
ret = keycode
if isinstance(ret, list):
ret = "|".join(ret)
return ret
class InputMonitor(threading.Thread):
def __init__(self, mqttclient, device, topic):
super(InputMonitor, self).__init__()
self.mqttclient = mqttclient
self.device = evdev.InputDevice(device)
self.topic = topic + '/state' # state topic
self.config = topic + '/config' # config topic for HA autodiscovery
config = {
"name": MQTTCFG["name"],
"state_topic": self.topic,
"icon": "mdi:code-json"
}
msg_config = json.dumps(config)
self.mqttclient.publish(self.config, msg_config)
log("Sending configuration for autodiscovery to %s" % (self.config))
log("Monitoring %s and sending to topic %s" % (device, self.topic))
def run(self):
global key_state
# Grab the input device to avoid keypresses also going to the
# Linux console (and attempting to login)
self.device.grab()
for event in self.device.read_loop():
if event.type == evdev.ecodes.EV_KEY:
k = evdev.categorize(event)
set_modifier(k.keycode, k.keystate)
if not is_modifier(k.keycode) and not is_ignore(k.keycode):
if k.keystate == 1:
msg = {
"key": concat_multikeys(k.keycode) +
get_modifiers(),
"devicePath": self.device.path
}
msg_json = json.dumps(msg)
self.mqttclient.publish(self.topic, msg_json)
# log what we publish
log("Device '%s', published message %s" %
(self.device.path, msg_json))
if __name__ == "__main__":
try:
Watcher()
config_filename = "config.local.json"
config_file = Path(config_filename)
if not config_file.is_file():
config_filename = "config.json"
log("Loading config from '%s'" % config_filename)
MQTTCFG = json.load(
open(config_filename)
)
CLIENT = "evmqtt_{hostname}_{time}".format(
hostname=hostname(), time=time()
)
MQ = MQTTClient(CLIENT, MQTTCFG)
MQ.start()
topic = MQTTCFG["topic"]
devices = MQTTCFG["devices"]
available_devices = [evdev.InputDevice(
path) for path in evdev.list_devices()]
log("Found %s available devices:" % len(available_devices))
for device in available_devices:
log("Path:'%s', Name: '%s'" % (device.path, device.name))
IM = [InputMonitor(MQ.mqttclient, device, topic) for device in devices]
for monitor in IM:
monitor.start()
except (OSError, KeyError) as er:
log("Exception: %s" % er)