xteink-plugins

This commit is contained in:
justin committed 2026-04-01 16:21:24 -07:00
commit 61f6e61a54
11 files changed
+744

No files matched your search

+213
View File
@@ -0,0 +1,213 @@
#!/usr/bin/env python3
import os
import sys
import shutil
import subprocess
import glob
import importlib.util
REPO_URL = "https://github.com/crosspoint-reader/crosspoint-reader.git"
REPO_DIR = "crosspoint-reader"
PLUGINS_DIR = "plugins"
def run(cmd, cwd=None, check=True):
print(f" $ {cmd}")
return subprocess.run(cmd, shell=True, cwd=cwd, check=check)
def clone_repo():
print("[1/3] Cloning CrossPoint repository...")
if os.path.exists(REPO_DIR):
print(f" Removing existing '{REPO_DIR}'...")
shutil.rmtree(REPO_DIR)
run(f"git clone --recursive {REPO_URL} {REPO_DIR}")
def run_plugin_patch(plugin_dir: str) -> bool:
patch_path = os.path.join(plugin_dir, "patch.py")
if not os.path.exists(patch_path):
print(f" WARNING: No patch.py found in {plugin_dir}, skipping.")
return False
spec = importlib.util.spec_from_file_location("patch", patch_path)
module = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(module)
if hasattr(module, "patch"):
module.patch(os.path.abspath(REPO_DIR))
return True
except Exception as exc:
print(f" ERROR while running patch.py for {plugin_dir}: {exc}")
return False
def apply_plugins():
print("[2/3] Applying plugins...")
if not os.path.exists(PLUGINS_DIR):
print(f" No '{PLUGINS_DIR}' directory found — skipping plugin step.")
return
plugin_dirs = sorted(
d for d in os.listdir(PLUGINS_DIR)
if os.path.isdir(os.path.join(PLUGINS_DIR, d))
)
if not plugin_dirs:
print(" No plugin directories found.")
return
for plugin_name in plugin_dirs:
plugin_path = os.path.join(PLUGINS_DIR, plugin_name)
print(f"\n Plugin: {plugin_name}")
answer = input(f" Install '{plugin_name}'? [Y/n]: ").strip().lower()
if answer in ("", "y", "yes"):
print(f" Installing {plugin_name}...")
success = run_plugin_patch(plugin_path)
if success:
print(f" ✓ {plugin_name} installed.")
else:
print(f" ✗ {plugin_name} failed or was skipped.")
else:
print(f" Skipping {plugin_name}.")
def list_likely_serial_ports():
patterns = [
"/dev/cu.usb*",
"/dev/tty.usb*",
"/dev/cu.wchusb*",
"/dev/tty.wchusb*",
"/dev/cu.SLAB*",
"/dev/tty.SLAB*",
"/dev/ttyACM*",
"/dev/ttyUSB*",
]
ports = []
for pattern in patterns:
ports.extend(glob.glob(pattern))
return sorted(set(ports))
def detect_serial_ports():
port_map: dict[str, str] = {}
try:
import serial.tools.list_ports
usb_keywords = {
"usb", "cp210", "ftdi", "ch340", "ch9102", "esp",
"serial", "uart", "prolific", "silabs",
}
skip_keywords = {"bluetooth", "bt-", "btle", "airpods", "handsfree"}
for port in serial.tools.list_ports.comports():
combined = (port.device + " " + (port.description or "") +
" " + (port.hwid or "")).lower()
if any(s in combined for s in skip_keywords):
continue
if any(k in combined for k in usb_keywords):
port_map[port.device] = port.description or "USB Serial Device"
except ImportError:
pass
for device in list_likely_serial_ports():
if device not in port_map:
port_map[device] = "Detected via glob pattern"
return sorted(port_map.items())
def prompt_for_upload_port():
print("\n[3/3] Select serial port for flashing...")
max_retries = 3
for attempt in range(1, max_retries + 1):
print(f"\n Attempt {attempt}/{max_retries}")
print(" Ensure your xteink device is connected via USB and awake.")
input(" Press Enter when ready... ")
ports = detect_serial_ports()
if ports:
cu_ports = [(d, desc) for d, desc in ports if d.startswith("/dev/cu.")]
recommended = cu_ports[0][0] if cu_ports else ports[0][0]
print(f"\n Found {len(ports)} serial port(s):")
for i, (device, desc) in enumerate(ports, 1):
tag = " ← recommended" if device == recommended else ""
print(f" {i}. {device} — {desc}{tag}")
choice = input(f"\n Press ENTER to use {recommended}, or enter 1-{len(ports)}: ").strip()
if not choice:
print(f" Selected: {recommended}")
return recommended
if choice.isdigit():
idx = int(choice) - 1
if 0 <= idx < len(ports):
print(f" Selected: {ports[idx][0]}")
return ports[idx][0]
print(" Invalid choice, please try again.")
else:
print(" No USB serial ports detected.")
if attempt < max_retries:
print(" Make sure the device is connected, powered on, and awake.")
print("\n Could not auto-detect a port.")
print(" Common names: macOS: /dev/cu.usb* Linux: /dev/ttyUSB* Windows: COMx")
while True:
port = input("\n Enter port manually (or 'quit' to exit): ").strip()
if port.lower() == "quit":
sys.exit(1)
if port:
return port
print(" Please enter a valid port name.")
def build_and_flash():
print("\nBuilding firmware with PlatformIO...")
run("pio run --environment default", cwd=REPO_DIR)
port = prompt_for_upload_port()
print(f"\n Flashing to {port}...")
run(
f"pio run --target upload --environment default --upload-port {port}",
cwd=REPO_DIR,
)
def main():
print("=" * 60)
print(" xteink Plugin Installer")
print("=" * 60)
print()
print(" This script modifies and flashes custom firmware to your")
print(" device. The author of this script (Justin Oros) accepts")
print(" no responsibility for any damage that may occur to your")
print(" device as a result of using this installer.")
print()
answer = input(" Do you wish to proceed? [Y/n]: ").strip().lower()
if answer not in ("", "y", "yes"):
print(" Aborted.")
sys.exit(0)
print()
clone_repo()
apply_plugins()
build_and_flash()
print("\nDone.")
if __name__ == "__main__":
main()