Initial release of Predator Control 1.0.0
PredatorSense replacement for the Acer Predator Helios 16 (PH16-71) on Linux: - predatord: root service for profiles, fans (incl. temperature curves), keyboard RGB, lightbar and battery health; restores settings at boot and after resume, switches profiles on AC/battery changes - Qt desktop app with dashboard, fan curve editor and per-key RGB studio - Plasma 6 panel widget with quick controls - predatorctl command line tool - predator_wmi kernel module (DKMS) as a minimal WMI method bridge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
"""predatorctl – command line interface for Predator Control.
|
||||
|
||||
Examples:
|
||||
predatorctl status
|
||||
predatorctl profile performance
|
||||
predatorctl fans max | auto | custom 60 70 | curve
|
||||
predatorctl kbd static '#ff0000' --brightness 255
|
||||
predatorctl kbd off
|
||||
predatorctl lightbar wave --color '#00ffcc' --speed 7
|
||||
predatorctl battery-limit on
|
||||
predatorctl open rgb (opens the main window on a given page)
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from .client import Client, DaemonError, read_state
|
||||
|
||||
|
||||
def _bool(v):
|
||||
return v.lower() in ("1", "on", "true", "yes")
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
p = argparse.ArgumentParser(prog="predatorctl", description="Predator Control CLI")
|
||||
sub = p.add_subparsers(dest="cmd", required=True)
|
||||
|
||||
sub.add_parser("status", help="show status")
|
||||
sp = sub.add_parser("state", help="raw state as JSON")
|
||||
sp.add_argument("--cached", action="store_true", help="read state.json instead of asking the daemon")
|
||||
sub.add_parser("restore", help="re-apply saved settings")
|
||||
|
||||
sp = sub.add_parser("profile", help="set power profile")
|
||||
sp.add_argument("profile")
|
||||
sp = sub.add_parser("profile-cycle", help="switch to the next profile")
|
||||
|
||||
sp = sub.add_parser("fans", help="fan mode")
|
||||
sp.add_argument("mode", choices=["auto", "max", "custom", "curve"])
|
||||
sp.add_argument("cpu", nargs="?", type=int)
|
||||
sp.add_argument("gpu", nargs="?", type=int)
|
||||
|
||||
sp = sub.add_parser("kbd", help="keyboard lighting (effect name, 'on' or 'off')")
|
||||
sp.add_argument("effect")
|
||||
sp.add_argument("color", nargs="?")
|
||||
sp.add_argument("--brightness", "-b", type=int)
|
||||
sp = sub.add_parser("kbd-brightness", help="keyboard brightness 0-255 or +/-step")
|
||||
sp.add_argument("value")
|
||||
sp = sub.add_parser("preset", help="load a keyboard preset")
|
||||
sp.add_argument("name")
|
||||
|
||||
sp = sub.add_parser("lightbar", help="Lightbar")
|
||||
sp.add_argument("mode", nargs="?")
|
||||
sp.add_argument("--color", "-c")
|
||||
sp.add_argument("--brightness", "-b", type=int)
|
||||
sp.add_argument("--speed", "-s", type=int)
|
||||
sp.add_argument("--sync", choices=["on", "off"])
|
||||
|
||||
sp = sub.add_parser("battery-limit", help="80 %% charge limit")
|
||||
sp.add_argument("value", choices=["on", "off"])
|
||||
|
||||
sp = sub.add_parser("open", help="open the main window")
|
||||
sp.add_argument("page", nargs="?", default="home")
|
||||
|
||||
a = p.parse_args(argv)
|
||||
|
||||
if a.cmd == "open":
|
||||
subprocess.Popen(["predator-control", "--page", a.page], start_new_session=True,
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return 0
|
||||
if a.cmd == "state" and a.cached:
|
||||
print(json.dumps(read_state()))
|
||||
return 0
|
||||
|
||||
c = Client()
|
||||
try:
|
||||
if a.cmd == "status":
|
||||
st = c.call("state")
|
||||
s, b = st["sensors"], st["battery"]
|
||||
lbl = st["profile_labels"].get(st["profile"], st["profile"])
|
||||
print("Profile: %s" % lbl)
|
||||
print("CPU: %s °C %s %% fan %s RPM" % (s["cpu_temp"], st["cpu_load"], s["cpu_fan"]))
|
||||
print("GPU: %s fan %s RPM" % ("%s °C" % s["gpu_temp"] if s["gpu_temp"] else "asleep", s["gpu_fan"]))
|
||||
print("Fan mode: %s" % st["config"]["fans"]["mode"])
|
||||
print("Battery: %s %% (%s), AC: %s" % (b.get("capacity"), b.get("status"), "yes" if b["ac_online"] else "no"))
|
||||
k = st["config"]["keyboard"]
|
||||
print("Keyboard: %s %s brightness %s" % (k["effect"], k["color"], k["brightness"]))
|
||||
for k, v in st["errors"].items():
|
||||
print("Error [%s]: %s" % (k, v))
|
||||
elif a.cmd == "state":
|
||||
print(json.dumps(c.call("state")))
|
||||
elif a.cmd == "restore":
|
||||
c.call("restore")
|
||||
elif a.cmd == "profile":
|
||||
c.call("set_profile", profile=a.profile)
|
||||
elif a.cmd == "profile-cycle":
|
||||
st = c.call("state")
|
||||
ch = st["profile_choices"]
|
||||
nxt = ch[(ch.index(st["profile"]) + 1) % len(ch)] if st["profile"] in ch else ch[0]
|
||||
c.call("set_profile", profile=nxt)
|
||||
print(st["profile_labels"].get(nxt, nxt))
|
||||
elif a.cmd == "fans":
|
||||
args = {"mode": a.mode}
|
||||
if a.cpu is not None:
|
||||
args["cpu"] = a.cpu
|
||||
if a.gpu is not None:
|
||||
args["gpu"] = a.gpu
|
||||
c.call("set_fans", **args)
|
||||
elif a.cmd == "kbd":
|
||||
args = {"effect": a.effect}
|
||||
if a.color:
|
||||
args["color"] = a.color if a.color.startswith("#") else "#" + a.color
|
||||
if a.brightness is not None:
|
||||
args["brightness"] = a.brightness
|
||||
c.call("set_keyboard", **args)
|
||||
elif a.cmd == "kbd-brightness":
|
||||
cur = c.call("state")["config"]["keyboard"]["brightness"]
|
||||
v = a.value
|
||||
val = cur + int(v) if v[0] in "+-" else int(v)
|
||||
c.call("set_keyboard", brightness=max(0, min(255, val)))
|
||||
elif a.cmd == "preset":
|
||||
c.call("load_preset", name=a.name)
|
||||
elif a.cmd == "lightbar":
|
||||
args = {"mode": a.mode} if a.mode else {}
|
||||
if a.color:
|
||||
args["color"] = a.color if a.color.startswith("#") else "#" + a.color
|
||||
if a.brightness is not None:
|
||||
args["brightness"] = a.brightness
|
||||
if a.speed is not None:
|
||||
args["speed"] = a.speed
|
||||
if a.sync:
|
||||
args["sync_keyboard"] = a.sync == "on"
|
||||
c.call("set_lightbar", **args)
|
||||
elif a.cmd == "battery-limit":
|
||||
c.call("set_battery_limit", enabled=_bool(a.value))
|
||||
except DaemonError as e:
|
||||
print("Error: %s" % e, file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user