-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgrok_companion.py
More file actions
executable file
·158 lines (133 loc) · 4.22 KB
/
Copy pathgrok_companion.py
File metadata and controls
executable file
·158 lines (133 loc) · 4.22 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
#!/usr/bin/env python3
"""Bridge between Grok CLI and the M5 Atom Matrix status display."""
import glob
import os
import pty
import select
import signal
import subprocess
import sys
import termios
import threading
import time
import tty
import serial
DEFAULT_PORT = "/dev/cu.usbserial-855251E6E2"
BAUD_RATE = 115200
grok_process = None
ser = None
def find_serial_port():
if os.path.exists(DEFAULT_PORT):
return DEFAULT_PORT
matches = sorted(glob.glob("/dev/cu.usbserial-*"))
return matches[0] if matches else None
def send_status(status_char):
if ser is None:
return
try:
ser.write(status_char.encode())
ser.flush()
except Exception:
pass
def matrix_button_listener():
global grok_process
while True:
try:
if ser.in_waiting > 0:
line = ser.readline().decode("utf-8", errors="ignore").strip()
if line == "BUTTON_PRESSED":
if grok_process and grok_process.poll() is None:
print("\n[Atom Companion] Notbremse gedrückt — breche Grok ab...")
os.kill(grok_process.pid, signal.SIGINT)
send_status("E")
except Exception:
break
def connect_matrix():
global ser
port = find_serial_port()
if not port:
print("Fehler: Kein Atom-Matrix-Serial-Port gefunden.")
print("Prüfe USB-Verbindung und schließe die Arduino IDE.")
sys.exit(1)
try:
ser = serial.Serial(port, BAUD_RATE, timeout=0.1)
time.sleep(2)
send_status("I")
print(f"[Companion] Verbunden mit Atom Matrix auf {port}")
except Exception:
print(f"Fehler: Verbindung zum Atom Matrix auf {port} fehlgeschlagen.")
print("Prüfe USB-Verbindung oder ob die Arduino IDE den Port blockiert.")
sys.exit(1)
def bridge_pty(master_fd):
old_tty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
while True:
if grok_process.poll() is not None:
break
readable, _, _ = select.select([master_fd, sys.stdin], [], [], 0.1)
if master_fd in readable:
try:
output = os.read(master_fd, 4096)
except OSError:
break
if not output:
break
os.write(sys.stdout.fileno(), output)
if sys.stdin in readable:
try:
user_input = os.read(sys.stdin.fileno(), 4096)
except OSError:
break
if not user_input:
break
os.write(master_fd, user_input)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
def run_grok():
global grok_process
grok_args = sys.argv[1:]
if not grok_args:
grok_args = ["build"]
print(f"[Companion] Starte 'grok {' '.join(grok_args)}'...")
send_status("W")
master_fd = None
try:
master_fd, slave_fd = pty.openpty()
grok_process = subprocess.Popen(
["grok"] + grok_args,
stdin=slave_fd,
stdout=slave_fd,
stderr=slave_fd,
close_fds=True,
)
os.close(slave_fd)
bridge_pty(master_fd)
grok_process.wait()
if grok_process.returncode == 0:
print("\n[Companion] Grok erfolgreich beendet.")
send_status("S")
elif grok_process.returncode in (-signal.SIGINT, 2):
print("\n[Companion] Durch Hardware-Button abgebrochen.")
send_status("E")
else:
print(f"\n[Companion] Grok-Fehler (Exit Code: {grok_process.returncode})")
send_status("E")
except FileNotFoundError:
print("\nFehler: 'grok' wurde im PATH nicht gefunden.")
send_status("E")
finally:
if master_fd is not None:
os.close(master_fd)
time.sleep(4)
send_status("I")
def main():
connect_matrix()
threading.Thread(target=matrix_button_listener, daemon=True).start()
try:
run_grok()
finally:
if ser and ser.is_open:
ser.close()
if __name__ == "__main__":
main()