Rückmeldung vibration
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
import fcntl
|
||||
import glob
|
||||
import http.server
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import socketserver
|
||||
import struct
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
@@ -248,6 +251,62 @@ def set_delay(seconds):
|
||||
return seconds
|
||||
|
||||
|
||||
def rumble_controller(duration_ms=400):
|
||||
# Linux FF constants
|
||||
EV_FF = 0x15
|
||||
FF_RUMBLE = 0x50
|
||||
# EVIOCSFF = _IOW('E', 0x80, struct ff_effect) — ff_effect is 48 bytes on ARM64
|
||||
EVIOCSFF = (0x40000000 | (48 << 16) | (ord('E') << 8) | 0x80)
|
||||
|
||||
device_path = None
|
||||
for path in sorted(glob.glob('/dev/input/event*')):
|
||||
try:
|
||||
sys_name = f'/sys/class/input/{os.path.basename(path)}/device/name'
|
||||
with open(sys_name, encoding='utf-8') as f:
|
||||
name = f.read().strip()
|
||||
if any(k in name for k in ('Gamepad', 'F710', 'GAMEPAD')):
|
||||
device_path = path
|
||||
break
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if not device_path:
|
||||
raise RuntimeError('Kein Gamepad gefunden')
|
||||
|
||||
fd = os.open(device_path, os.O_RDWR)
|
||||
try:
|
||||
# struct ff_effect layout on ARM64 (48 bytes):
|
||||
# offset 0: type (u16)
|
||||
# offset 2: id (s16) — set to -1, kernel assigns
|
||||
# offset 4: direction (u16)
|
||||
# offset 6: trigger.button (u16)
|
||||
# offset 8: trigger.interval (u16)
|
||||
# offset 10: replay.length (u16, ms)
|
||||
# offset 12: replay.delay (u16)
|
||||
# offset 14: padding (2 bytes)
|
||||
# offset 16: union.rumble.strong_magnitude (u16)
|
||||
# offset 18: union.rumble.weak_magnitude (u16)
|
||||
# offset 20–47: zeros
|
||||
buf = bytearray(48)
|
||||
struct.pack_into('<H', buf, 0, FF_RUMBLE)
|
||||
struct.pack_into('<h', buf, 2, -1)
|
||||
struct.pack_into('<H', buf, 10, duration_ms)
|
||||
struct.pack_into('<H', buf, 16, 0xFFFF)
|
||||
struct.pack_into('<H', buf, 18, 0x8000)
|
||||
|
||||
fcntl.ioctl(fd, EVIOCSFF, buf)
|
||||
effect_id = struct.unpack_from('<h', buf, 2)[0]
|
||||
|
||||
# input_event on ARM64: sec(u64) + usec(u64) + type(u16) + code(u16) + value(s32) = 24 bytes
|
||||
os.write(fd, struct.pack('<QQHHi', 0, 0, EV_FF, effect_id, 1))
|
||||
time.sleep(duration_ms / 1000.0 + 0.1)
|
||||
os.write(fd, struct.pack('<QQHHi', 0, 0, EV_FF, effect_id, 0))
|
||||
except OSError as exc:
|
||||
raise RuntimeError(f'FF ioctl fehlgeschlagen: {exc}') from exc
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
|
||||
def restart_camera_service():
|
||||
subprocess.Popen(['systemctl', 'restart', 'exomy-camera-stream.service'])
|
||||
|
||||
@@ -659,6 +718,14 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
self.write_json(status_code, response)
|
||||
return
|
||||
|
||||
if self.path == '/api/rumble':
|
||||
try:
|
||||
rumble_controller(400)
|
||||
self.write_json(200, {'status': 'Vibration ausgelöst'})
|
||||
except RuntimeError as exc:
|
||||
self.write_json(500, {'status': str(exc)})
|
||||
return
|
||||
|
||||
actions = {
|
||||
'/api/restart-camera': {
|
||||
'handler': restart_camera_service,
|
||||
|
||||
Reference in New Issue
Block a user