- COSMIC Applet: Pfad gefixt, Desktop-File korrigiert, systemd-User-Service - Razer Naga Left Handed: Button-Swap-Daemon (evdev/uinput) + udev + systemd - superx-ctrl.py: Pfade gefixt (FROSTBAY_BLE, tablet-rgb.py) - tablet-rgb.py: Dynamische HIDRAW-Erkennung statt hartkodiertem Pfad - README: Status aktualisiert (Tablet RGB, Keyboard RGB, Mainboard RGB) - udev: 99-razer-naga-swap.rules für Hotplug
170 lines
No EOL
5.8 KiB
Python
170 lines
No EOL
5.8 KiB
Python
#!/usr/bin/env python3
|
|
"""superx-ctrl.py v4 — Frostbay cooling + RGB presets + Direct D-Bus writes."""
|
|
import json, sys, os, subprocess, time, dbus
|
|
|
|
FROSTBAY_BLE = os.path.expanduser("~/git.nepharius.at/superx-goes-arch/tools/frostbay_ble.py")
|
|
|
|
COOL_MODES = [
|
|
{"id": "off", "label": "Off", "icon": "power_settings_new"},
|
|
{"id": "silent", "label": "Silent", "icon": "ac_unit"},
|
|
{"id": "gentle", "label": "Gentle", "icon": "air"},
|
|
{"id": "strong", "label": "Strong", "icon": "speed"},
|
|
{"id": "extreme", "label": "Extreme", "icon": "rocket_launch"},
|
|
{"id": "smart", "label": "Smart", "icon": "auto_mode"},
|
|
]
|
|
|
|
RGB_PRESETS = [
|
|
{"id": 1, "label": "Mode 1"},
|
|
{"id": 2, "label": "Mode 2"},
|
|
{"id": 3, "label": "Mode 3"},
|
|
{"id": 4, "label": "Mode 4"},
|
|
{"id": 5, "label": "Mode 5"},
|
|
{"id": 6, "label": "Mode 6"},
|
|
{"id": 7, "label": "Mode 7"},
|
|
{"id": 8, "label": "Mode 8"},
|
|
{"id": 9, "label": "Aurora"},
|
|
{"id": 10, "label": "Mode 10"},
|
|
]
|
|
|
|
# Direct BLE access
|
|
BUS = dbus.SystemBus()
|
|
BASE = "/org/bluez/hci0/dev_C8_17_17_F5_C8_62"
|
|
CHAR = f"{BASE}/service003f/char0040"
|
|
|
|
def ble_read():
|
|
c = dbus.Interface(BUS.get_object("org.bluez", CHAR), "org.bluez.GattCharacteristic1")
|
|
return bytes(c.ReadValue({}))
|
|
|
|
def ble_write(data):
|
|
c = dbus.Interface(BUS.get_object("org.bluez", CHAR), "org.bluez.GattCharacteristic1")
|
|
c.WriteValue(dbus.Array(list(data), signature="y"), {})
|
|
|
|
def make_buff():
|
|
"""Build a fresh 64-byte buffer based on current state."""
|
|
cur = ble_read()
|
|
b = bytearray(64)
|
|
b[:] = cur[:64]
|
|
b[0] = 0xC1
|
|
b[1] = 0x02 # write flag
|
|
b[3] = 0x01 # follow mode
|
|
b[4] = 0xFF # custom mode (keep fans/pump in current state)
|
|
return b
|
|
|
|
def run_frostbay(*args):
|
|
try:
|
|
r = subprocess.run([sys.executable, FROSTBAY_BLE] + list(args),
|
|
capture_output=True, text=True, timeout=10)
|
|
return {"ok": True, "stdout": r.stdout}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
def cmd_status():
|
|
r = run_frostbay("raw")
|
|
return {"ok": r["ok"], "modes": COOL_MODES, "rgb_presets": RGB_PRESETS}
|
|
|
|
def cmd_mode(mid):
|
|
r = run_frostbay("mode", mid)
|
|
return {"ok": r["ok"], "mode": mid}
|
|
|
|
def cmd_custom(fan, pump):
|
|
r = run_frostbay("mode", "custom", "--fan", str(fan), "--pump", str(pump))
|
|
return {"ok": r["ok"], "fan": fan, "pump": pump}
|
|
|
|
def cmd_rgb_preset(num):
|
|
"""Two-step direct BLE write: brightness first, then preset."""
|
|
try:
|
|
# Step 1: Brightness/switch AE
|
|
b = make_buff()
|
|
b[16] = 0xFD
|
|
b[17] = 0x07 # all strips
|
|
b[18] = 0x05 # speed
|
|
b[19] = 0x04 # brightness max
|
|
ble_write(bytes(b))
|
|
time.sleep(0.3)
|
|
|
|
# Step 2: Preset number
|
|
b2 = make_buff()
|
|
b2[16] = num & 0xFF
|
|
ble_write(bytes(b2))
|
|
time.sleep(0.3)
|
|
|
|
return {"ok": True, "preset": num}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
def cmd_rgb_brightness(switch, speed, brightness):
|
|
try:
|
|
b = make_buff()
|
|
b[16] = 0xFD
|
|
b[17] = switch & 0xFF
|
|
b[18] = speed & 0xFF
|
|
b[19] = brightness & 0xFF
|
|
ble_write(bytes(b))
|
|
return {"ok": True}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
def cmd_rgb_off():
|
|
try:
|
|
b = make_buff()
|
|
b[16] = 0xFD
|
|
b[17] = 0x00 # switch=0 = all off
|
|
ble_write(bytes(b))
|
|
return {"ok": True}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
TABLET_CMD = os.path.expanduser("~/git.nepharius.at/superx-goes-arch/tools/tablet-rgb.py")
|
|
|
|
def cmd_tablet(*args):
|
|
"""Control tablet RGB via HIDRAW."""""
|
|
try:
|
|
r = subprocess.run([sys.executable, TABLET_CMD] + list(args),
|
|
capture_output=True, text=True, timeout=10)
|
|
return json.loads(r.stdout)
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
def cmd_ec_status():
|
|
"""Read EC turbo status via sudo."""
|
|
try:
|
|
r = subprocess.run(["sudo", "/home/nepharius/.local/bin/ec-read.py"],
|
|
capture_output=True, text=True, timeout=5)
|
|
if r.returncode == 0:
|
|
return json.loads(r.stdout)
|
|
return {"ok": False, "error": r.stderr}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
def cmd_ec_turbo(on=True):
|
|
"""Set turbo via EC (writes to offset 0x30)."""
|
|
try:
|
|
val = "06" if on else "00"
|
|
r = subprocess.run(
|
|
["sh", "-c", f"""printf '\\x{val}' | sudo dd of=/sys/kernel/debug/ec/ec0/io bs=1 seek=$((0x30)) count=1 conv=notrunc 2>/dev/null"""],
|
|
capture_output=True, text=True, timeout=5
|
|
)
|
|
return {"ok": True, "turbo": on}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2:
|
|
print(json.dumps({"ok": False, "error": "Need command"}))
|
|
sys.exit(1)
|
|
|
|
cmd = sys.argv[1]
|
|
if cmd == "status": result = cmd_status()
|
|
elif cmd == "mode" and len(sys.argv) >= 3: result = cmd_mode(sys.argv[2])
|
|
elif cmd == "custom" and len(sys.argv) >= 4: result = cmd_custom(int(sys.argv[2]), int(sys.argv[3]))
|
|
elif cmd == "rgb-preset" and len(sys.argv) >= 3: result = cmd_rgb_preset(int(sys.argv[2]))
|
|
elif cmd == "rgb-brightness" and len(sys.argv) >= 5: result = cmd_rgb_brightness(int(sys.argv[2]), int(sys.argv[3]), int(sys.argv[4]))
|
|
elif cmd == "rgb-on": result = cmd_rgb_brightness(7, 5, 4)
|
|
elif cmd == "rgb-off": result = cmd_rgb_off()
|
|
elif cmd == "tablet" and len(sys.argv) >= 3: result = cmd_tablet(*sys.argv[2:])
|
|
elif cmd == "ec-status": result = cmd_ec_status()
|
|
elif cmd == "ec-turbo-on": result = cmd_ec_turbo(True)
|
|
elif cmd == "ec-turbo-off": result = cmd_ec_turbo(False)
|
|
else: result = {"ok": False, "error": f"Unknown: {cmd}"}
|
|
|
|
print(json.dumps(result)) |