Skill: wangjiaocheng/linux-system-controller (version 1.0.0)

--- name: system-controller description: Linux系统控制能力包；当用户需要控制桌面窗口、管理进程、调整音量亮度、管理电源网络、与串口设备通信、控制智能家居设备、进行鼠标键盘自动化、截图OCR或视觉识别时使用 dependency: python: - pyserial>=3.5 - requests>=2.28.0 - pyautogui>=0.9.54 - pillow>=9.0.0 - pytesseract>=0.3.10 system: - apt-get update && apt-get install -y xdotool wmctrl tesseract-ocr alsa-utils --- # System Controller Linux系统统一控制接口，支持桌面应用、系统硬件、串口设备和IoT平台的综合控制。 ## 架构 ``` 用户请求 → 自然语言理解 → 脚本执行 → 系统操作 ``` 六个控制模块，每个模块都有专用的Python脚本： | 模块 | 脚本 | 功能范围 | |------|------|----------| | Window Manager | `scripts/window_manager.py` | 桌面窗口控制 | | Process Manager | `scripts/process_manager.py` | 系统进程管理 | | Hardware Controller | `scripts/hardware_controller.py` | 系统硬件设置 | | Serial Communication | `scripts/serial_comm.py` | Arduino / 串口设备 | | IoT Controller | `scripts/iot_controller.py` | 智能家居 / HTTP APIs | | GUI Controller | `scripts/gui_controller.py` | 鼠标、键盘、截图、OCR | 所有脚本都是独立的CLI工具，无脚本间依赖。 ## 执行模型 ### 前置条件 - **操作系统**: Linux (支持X11/Wayland) - **Python**: 3.6+ - **必需工具**: - `xdotool`: 窗口和GUI自动化 - `wmctrl`: 窗口管理 - `tesseract-ocr`: 文字识别 - `alsa-utils`: 音量控制 - **Python依赖**: 自动安装（见dependency） ### 脚本路径 所有脚本位于：`scripts/` ### 执行模式 使用 `execute_command` 运行脚本。模式： ``` python3 scripts/<script_name>.py <action> [flags] ``` ### 安全规则 1. **执行破坏性操作前必须用户确认**（关机、重启、结束进程、关闭窗口、禁用网卡） 2. **先列表/查询再操作**。例如：先`list`再`close`，先`list --name`再`kill` 3. **电源操作前警告用户**（关机、重启、睡眠、休眠）并要求明确确认 4. **禁用关键网卡前警告**（正在使用的互联网连接） 5. **串口通信前先列表端口**以确认正确的端口名称 ## 模块详情 ### 1. Window Manager (`window_manager.py`) 通过xdotool和wmctrl控制桌面应用窗口。 **功能**: list, activate, close, minimize, maximize, resize, move **决策流程**: 1. 用户说"打开/关闭/聚焦X" → `list`查找窗口 → 确认 → 执行操作 2. 用户说"调整/移动X" → `list`查找窗口ID → `resize`/`move` 3. 用户说"发送/输入X到Y" → `activate`目标窗口 → `type` **常见示例**: - "打开记事本" → `process_manager.py start "gedit"` - "关闭Chrome" → `window_manager.py list` → 查找Chrome → `window_manager.py close --title "Chrome"` - "把微信调到前台" → `window_manager.py activate --title "WeChat"` - "全屏当前窗口" → `window_manager.py maximize --title "..."` ### 2. Process Manager (`process_manager.py`) 列表、启动、停止和监控系统进程。 **功能**: list, kill, start, info, system **决策流程**: 1. 用户说"查看进程" → `list` 或 `list --name` 2. 用户说"结束X进程" → `list --name X` → 确认 → `kill --name X` 3. 用户说"启动X" → `start "X"` 4. 用户说"系统状态" → `system` **常见示例**: - "有哪些程序在运行" → `process_manager.py system` - "关掉所有gedit" → `process_manager.py kill --name gedit` - "启动VS Code" → `process_manager.py start "code"` ### 3. Hardware Controller (`hardware_controller.py`) 通过Linux系统工具控制硬件设置。 **功能**: - 音量: get, set, mute, unmute - 屏幕: 亮度(get/set), 显示器信息 - 电源: lock, sleep, hibernate, shutdown, restart - 网络: 列表网卡, 启用/禁用, WiFi扫描, 网络信息 - USB: 列出设备 **决策流程**: 1. 用户说"音量/声音" → 音量命令 2. 用户说"亮度/屏幕" → 屏幕/亮度命令 3. 用户说"关机/锁屏/睡眠" → 电源命令（**总是确认**） 4. 用户说"网络/WiFi" → 网络命令 5. 用户说"USB" → usb list **常见示例**: - "把音量调到50" → `hardware_controller.py volume set --level 50` - "静音" → `hardware_controller.py volume mute` - "屏幕调暗一点" → `hardware_controller.py screen brightness --level 30` - "锁屏" → `hardware_controller.py power lock` - "扫描WiFi" → `hardware_controller.py network wifi` ### 4. Serial Communication (`serial_comm.py`) 通过pyserial与Arduino、ESP32等串口设备通信。 **功能**: 列表端口, 检测波特率, 发送, 接收, 聊天, 监听 **决策流程**: 1. 用户说"串口/Arduino/tty" → 先`list`端口 2. 用户说"发送到Arduino" → `send --port /dev/ttyUSB0 --data "..."` 3. 用户说"读取传感器" → `chat --port /dev/ttyUSB0 --data "READ"` **自动安装**: 首次使用时自动安装pyserial。 **常见示例**: - "有哪些串口" → `serial_comm.py list` - "给Arduino发指令开灯" → `serial_comm.py send --port /dev/ttyUSB0 --data "LED_ON"` - "读取温度传感器" → `serial_comm.py chat --port /dev/ttyUSB0 --data "GET_TEMP"` - "监听串口数据" → `serial_comm.py monitor --port /dev/ttyUSB0 --duration 30` ### 5. IoT Controller (`iot_controller.py`) 通过Home Assistant REST API、Mijia或通用HTTP端点控制智能家居设备。 **功能**: - Home Assistant: 列出实体, 获取状态, 开启/关闭/切换, 调用任何服务 - 通用HTTP: GET, POST, PUT到任何REST端点 - Mijia: 设备发现指导 **决策流程**: 1. 用户提到"智能家居/Home Assistant/灯光/温度" → IoT命令 2. 用户说"控制设备/开关灯" → 需要用户提供URL和token 3. 用户说"调用API" → 通用HTTP命令 **自动安装**: 首次使用时自动安装requests。 **常见示例**: - "列出所有智能设备" → `iot_controller.py homeassistant --url ... --token ... list` - "打开客厅灯" → `iot_controller.py homeassistant --url ... --token ... on --entity-id light.living_room` - "关掉卧室空调" → `iot_controller.py homeassistant --url ... --token ... off --entity-id climate.bedroom` - "调用这个API" → `iot_controller.py http --url ... get --path ...` ### 6. GUI Controller (`gui_controller.py`) 完整的GUI自动化：鼠标控制、键盘输入、截图、OCR和视觉识别。 **功能**: - 鼠标: 移动, 点击(左/右/双击), 拖拽, 滚动, 获取位置 - 键盘: 输入文本, 按热键, 按键组合 - 截图: 全屏, 区域, 活动窗口, 列出已保存截图 - OCR: 从屏幕区域提取文字（需要tesseract-ocr） - 视觉: 在屏幕上查找图像模板, 点击图像, 按颜色查找, 获取像素颜色 **自动安装**: 首次使用时自动安装pyautogui和pillow。 **决策流程**: 1. 用户说"click/点击" → 确定位置(坐标/图像模板/颜色) → 点击 2. 用户说"type/输入文字" → `keyboard type --text "..."` 3. 用户说"screenshot/截图" → `screenshot full` 或 `screenshot active-window` 4. 用户说"read screen/OCR/识别文字" → 在相关区域 `visual ocr` 5. 用户说"find/查找图标" → `visual find --template "icon.png"` 6. 用户说"scroll/滚动" → `mouse scroll` 7. 用户说"drag/拖拽" → `mouse drag --start-x ... --start-y ... --end-x ... --end-y ...` **常见示例**: - "截图" → `gui_controller.py screenshot full` - "截取当前窗口" → `gui_controller.py screenshot active-window` - "鼠标移到(500,300)" → `gui_controller.py mouse move --x 500 --y 300` - "点击(500,300)" → `gui_controller.py mouse click --x 500 --y 300` - "右键点击" → `gui_controller.py mouse right-click --x 500 --y 300` - "双击" → `gui_controller.py mouse double-click --x 500 --y 300` - "拖拽文件从(100,200)到(500,400)" → `gui_controller.py mouse drag --start-x 100 --start-y 200 --end-x 500 --end-y 400` - "向下滚动" → `gui_controller.py mouse scroll --direction down --clicks 10` - "输入Hello World" → `gui_controller.py keyboard type --text "Hello World"` - "按Ctrl+C" → `gui_controller.py keyboard press --keys "ctrl+c"` - "按Alt+Tab" → `gui_controller.py keyboard press --keys "alt+tab"` - "识别屏幕上的文字" → `gui_controller.py visual ocr` - "识别(100,100,800,600)区域的文字" → `gui_controller.py visual ocr --x 100 --y 100 --width 800 --height 600` - "在屏幕上找这个图标" → `gui_controller.py visual find --template "button.png"` - "找到并点击这个图片" → `gui_controller.py visual click-image --template "submit.png"` - "获取(200,200)位置的颜色" → `gui_controller.py visual pixel --x 200 --y 200` - "获取鼠标位置" → `gui_controller.py mouse position` - "获取屏幕分辨率" → `gui_controller.py screenshot size` **安全规则**: - 鼠标和键盘操作影响屏幕上的任何内容 - **不确定时先截图** - 已启用**故障保护**：将鼠标移到任何屏幕角落将中止所有pyautogui操作 - OCR和图像识别是尽力而为 - 准确度取决于屏幕分辨率、语言和图像质量 - OCR最佳效果需要安装tesseract-ocr ## 处理未知设备 当用户请求控制未直接涵盖的设备或软件时： 1. **检查是否可以作为进程启动**: 使用 `process_manager.py start "app_name"` 2. **检查是否有窗口**: 使用 `window_manager.py list` 查找 3. **截图查看**: 使用 `gui_controller.py screenshot full` 查看屏幕内容 4. **使用OCR读取文字**: 使用 `gui_controller.py visual ocr` 从屏幕提取文字 5. **点击图像匹配**: 保存模板，然后 `gui_controller.py visual click-image --template icon.png` 6. **使用鼠标/键盘**: `gui_controller.py mouse click --x 100 --y 200` 直接控制 7. **检查是否有API**: 使用 `iot_controller.py http` 交互 8. **检查是否USB连接**: 使用 `hardware_controller.py usb list` 然后 `serial_comm.py list` 9. **建议替代方案**: 如果以上都不行，解释限制并建议自定义脚本 ## 快速参考 详细命令语法，阅读 `references/command_reference.md`。

path: scripts/gui_controller.py
content: #!/usr/bin/env python3
"""
GUI Controller - Mouse, keyboard, screenshot, and visual control for Linux.

Capabilities:
  - Mouse: move, click, double-click, right-click, drag, scroll, get position
  - Keyboard: type text, press hotkeys, key combinations
  - Screenshot: full screen, region, save to file
  - OCR: extract text from screen regions (requires tesseract-ocr)
  - Visual: find image/pattern on screen, click by color

Requirements: Linux with X11, pyautogui, pillow, tesseract-ocr (optional for OCR)
"""

import sys
import os
import json
import argparse

if sys.stdout.encoding != 'utf-8':
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
if sys.stderr.encoding != 'utf-8':
    sys.stderr.reconfigure(encoding='utf-8', errors='replace')


def check_pyautogui():
    """Check if pyautogui is available."""
    try:
        import pyautogui
        pyautogui.PAUSE = 0.1  # Small delay for better reliability
        pyautogui.FAILSAFE = True  # Move mouse to corner to abort
        return pyautogui
    except ImportError:
        print("Installing pyautogui...", file=sys.stderr)
        import subprocess
        subprocess.check_call(
            [sys.executable, "-m", "pip", "install", "pyautogui", "-q"],
            stdout=subprocess.DEVNULL
        )
        import pyautogui
        pyautogui.PAUSE = 0.1
        pyautogui.FAILSAFE = True
        return pyautogui


def check_pillow():
    """Check if pillow is available."""
    try:
        from PIL import Image, ImageGrab
        return Image, ImageGrab
    except ImportError:
        print("Installing pillow...", file=sys.stderr)
        import subprocess
        subprocess.check_call(
            [sys.executable, "-m", "pip", "install", "pillow", "-q"],
            stdout=subprocess.DEVNULL
        )
        from PIL import Image, ImageGrab
        return Image, ImageGrab


def check_tesseract():
    """Check if pytesseract is available."""
    try:
        import pytesseract
        return pytesseract
    except ImportError:
        print("Installing pytesseract...", file=sys.stderr)
        import subprocess
        subprocess.check_call(
            [sys.executable, "-m", "pip", "install", "pytesseract", "-q"],
            stdout=subprocess.DEVNULL
        )
        import pytesseract
        return pytesseract


# ============ Mouse ============

def mouse_move(x, y):
    """Move mouse to coordinates."""
    pyautogui = check_pyautogui()
    pyautogui.moveTo(x, y)
    return {"success": True, "position": {"x": x, "y": y}}


def mouse_click(x=None, y=None, button='left'):
    """Click mouse at coordinates."""
    pyautogui = check_pyautogui()
    if x is not None and y is not None:
        pyautogui.click(x, y, button=button)
    else:
        pyautogui.click(button=button)
    pos = pyautogui.position()
    return {"success": True, "position": {"x": pos.x, "y": pos.y}, "button": button}


def mouse_right_click(x=None, y=None):
    """Right click mouse."""
    return mouse_click(x, y, button='right')


def mouse_double_click(x=None, y=None):
    """Double click mouse."""
    pyautogui = check_pyautogui()
    if x is not None and y is not None:
        pyautogui.doubleClick(x, y)
    else:
        pyautogui.doubleClick()
    pos = pyautogui.position()
    return {"success": True, "position": {"x": pos.x, "y": pos.y}}


def mouse_drag(start_x, start_y, end_x, end_y, duration=0.5):
    """Drag mouse from start to end."""
    pyautogui = check_pyautogui()
    pyautogui.dragTo(end_x, end_y, duration=duration, button='left')
    return {"success": True, "from": {"x": start_x, "y": start_y}, "to": {"x": end_x, "y": end_y}}


def mouse_scroll(direction='down', clicks=1):
    """Scroll mouse wheel."""
    pyautogui = check_pyautogui()
    amount = -clicks if direction == 'down' else clicks
    pyautogui.scroll(amount)
    return {"success": True, "direction": direction, "clicks": clicks}


def mouse_position():
    """Get current mouse position."""
    pyautogui = check_pyautogui()
    pos = pyautogui.position()
    return {"position": {"x": pos.x, "y": pos.y}}


# ============ Keyboard ============

def keyboard_type(text):
    """Type text."""
    pyautogui = check_pyautogui()
    pyautogui.typewrite(text, interval=0.01)
    return {"success": True, "text": text}


def keyboard_press(keys):
    """Press hotkey combination (e.g., 'ctrl+c', 'alt+tab')."""
    pyautogui = check_pyautogui()
    pyautogui.hotkey(*keys.split('+'))
    return {"success": True, "keys": keys}


# ============ Screenshot ============

def screenshot_full(output_file=None):
    """Take full screenshot."""
    Image, ImageGrab = check_pillow()
    screenshot = ImageGrab.grab()

    if output_file:
        screenshot.save(output_file)
        return {"success": True, "file": output_file, "size": screenshot.size}
    else:
        # Save to default location
        import time
        filename = f"screenshot_{int(time.time())}.png"
        filepath = os.path.join(os.getcwd(), filename)
        screenshot.save(filepath)
        return {"success": True, "file": filepath, "size": screenshot.size}


def screenshot_region(x, y, width, height, output_file=None):
    """Take screenshot of region."""
    Image, ImageGrab = check_pillow()
    screenshot = ImageGrab.grab(bbox=(x, y, x + width, y + height))

    if output_file:
        screenshot.save(output_file)
        return {"success": True, "file": output_file, "size": screenshot.size}
    else:
        import time
        filename = f"region_{int(time.time())}.png"
        filepath = os.path.join(os.getcwd(), filename)
        screenshot.save(filepath)
        return {"success": True, "file": filepath, "size": screenshot.size}


def screenshot_size():
    """Get screen size."""
    pyautogui = check_pyautogui()
    size = pyautogui.size()
    return {"width": size.width, "height": size.height}


# ============ OCR ============

def visual_ocr(x=None, y=None, width=None, height=None, lang='eng'):
    """Extract text from screen using OCR."""
    try:
        pytesseract = check_tesseract()
    except Exception:
        return {"error": "tesseract-ocr not installed. Install with: apt-get install tesseract-ocr"}

    Image, ImageGrab = check_pillow()

    if x is not None and y is not None and width is not None and height is not None:
        screenshot = ImageGrab.grab(bbox=(x, y, x + width, y + height))
    else:
        screenshot = ImageGrab.grab()

    try:
        text = pytesseract.image_to_string(screenshot, lang=lang)
        return {"success": True, "text": text.strip()}
    except Exception as e:
        return {"error": f"OCR failed: {e}"}


# ============ Visual ============

def visual_find(template_path, confidence=0.8):
    """Find image template on screen."""
    pyautogui = check_pyautogui()
    try:
        location = pyautogui.locateOnScreen(template_path, confidence=confidence)
        if location:
            return {
                "success": True,
                "location": {
                    "left": location.left,
                    "top": location.top,
                    "width": location.width,
                    "height": location.height,
                    "center": {"x": location.left + location.width // 2, "y": location.top + location.height // 2}
                }
            }
        else:
            return {"error": "Template not found on screen"}
    except Exception as e:
        return {"error": f"Failed to find template: {e}"}


def visual_click_image(template_path, confidence=0.8):
    """Find and click image template."""
    result = visual_find(template_path, confidence)
    if "error" in result:
        return result

    center = result["location"]["center"]
    pyautogui = check_pyautogui()
    pyautogui.click(center["x"], center["y"])
    return {"success": True, "clicked": center}


def visual_pixel(x, y):
    """Get pixel color at coordinates."""
    Image, ImageGrab = check_pillow()
    screenshot = ImageGrab.grab()
    pixel = screenshot.getpixel((x, y))

    # Convert RGB to hex
    color_hex = f"#{pixel[0]:02x}{pixel[1]:02x}{pixel[2]:02x}"
    return {"success": True, "position": {"x": x, "y": y}, "color": {"r": pixel[0], "g": pixel[1], "b": pixel[2], "hex": color_hex}}


def main():
    parser = argparse.ArgumentParser(description="GUI Controller for Linux")
    subparsers = parser.add_subparsers(dest='category', help='Category to control')

    # Mouse
    mouse_parser = subparsers.add_parser('mouse', help='Mouse control')
    mouse_subparsers = mouse_parser.add_subparsers(dest='action', help='Mouse action')
    mouse_subparsers.add_parser('position', help='Get mouse position')
    mouse_move_parser = mouse_subparsers.add_parser('move', help='Move mouse')
    mouse_move_parser.add_argument('--x', type=int, required=True)
    mouse_move_parser.add_argument('--y', type=int, required=True)
    mouse_click_parser = mouse_subparsers.add_parser('click', help='Click mouse')
    mouse_click_parser.add_argument('--x', type=int)
    mouse_click_parser.add_argument('--y', type=int)
    mouse_subparsers.add_parser('right-click', help='Right click')
    mouse_subparsers.add_parser('double-click', help='Double click')
    mouse_drag_parser = mouse_subparsers.add_parser('drag', help='Drag mouse')
    mouse_drag_parser.add_argument('--start-x', type=int, required=True)
    mouse_drag_parser.add_argument('--start-y', type=int, required=True)
    mouse_drag_parser.add_argument('--end-x', type=int, required=True)
    mouse_drag_parser.add_argument('--end-y', type=int, required=True)
    mouse_scroll_parser = mouse_subparsers.add_parser('scroll', help='Scroll')
    mouse_scroll_parser.add_argument('--direction', choices=['up', 'down'], default='down')
    mouse_scroll_parser.add_argument('--clicks', type=int, default=10)

    # Keyboard
    keyboard_parser = subparsers.add_parser('keyboard', help='Keyboard control')
    keyboard_subparsers = keyboard_parser.add_subparsers(dest='action', help='Keyboard action')
    keyboard_type_parser = keyboard_subparsers.add_parser('type', help='Type text')
    keyboard_type_parser.add_argument('--text', required=True)
    keyboard_press_parser = keyboard_subparsers.add_parser('press', help='Press keys')
    keyboard_press_parser.add_argument('--keys', required=True)

    # Screenshot
    screenshot_parser = subparsers.add_parser('screenshot', help='Screenshot')
    screenshot_subparsers = screenshot_parser.add_subparsers(dest='action', help='Screenshot action')
    screenshot_subparsers.add_parser('full', help='Full screen screenshot')
    screenshot_region_parser = screenshot_subparsers.add_parser('region', help='Region screenshot')
    screenshot_region_parser.add_argument('--x', type=int, required=True)
    screenshot_region_parser.add_argument('--y', type=int, required=True)
    screenshot_region_parser.add_argument('--width', type=int, required=True)
    screenshot_region_parser.add_argument('--height', type=int, required=True)
    screenshot_subparsers.add_parser('size', help='Get screen size')

    # Visual
    visual_parser = subparsers.add_parser('visual', help='Visual recognition')
    visual_subparsers = visual_parser.add_subparsers(dest='action', help='Visual action')
    visual_subparsers.add_parser('ocr', help='OCR')
    visual_find_parser = visual_subparsers.add_parser('find', help='Find template')
    visual_find_parser.add_argument('--template', required=True)
    visual_click_image_parser = visual_subparsers.add_parser('click-image', help='Click image')
    visual_click_image_parser.add_argument('--template', required=True)
    visual_pixel_parser = visual_subparsers.add_parser('pixel', help='Get pixel color')
    visual_pixel_parser.add_argument('--x', type=int, required=True)
    visual_pixel_parser.add_argument('--y', type=int, required=True)

    args = parser.parse_args()

    if not args.category:
        parser.print_help()
        sys.exit(1)

    result = {}

    # Mouse
    if args.category == 'mouse':
        if args.action == 'position':
            result = mouse_position()
        elif args.action == 'move':
            result = mouse_move(args.x, args.y)
        elif args.action == 'click':
            result = mouse_click(args.x, args.y)
        elif args.action == 'right-click':
            result = mouse_right_click(args.x, args.y)
        elif args.action == 'double-click':
            result = mouse_double_click(args.x, args.y)
        elif args.action == 'drag':
            result = mouse_drag(args.start_x, args.start_y, args.end_x, args.end_y)
        elif args.action == 'scroll':
            result = mouse_scroll(args.direction, args.clicks)

    # Keyboard
    elif args.category == 'keyboard':
        if args.action == 'type':
            result = keyboard_type(args.text)
        elif args.action == 'press':
            result = keyboard_press(args.keys)

    # Screenshot
    elif args.category == 'screenshot':
        if args.action == 'full':
            result = screenshot_full()
        elif args.action == 'region':
            result = screenshot_region(args.x, args.y, args.width, args.height)
        elif args.action == 'size':
            result = screenshot_size()

    # Visual
    elif args.category == 'visual':
        if args.action == 'ocr':
            result = visual_ocr()
        elif args.action == 'find':
            result = visual_find(args.template)
        elif args.action == 'click-image':
            result = visual_click_image(args.template)
        elif args.action == 'pixel':
            result = visual_pixel(args.x, args.y)

    print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()


path: scripts/hardware_controller.py
content: #!/usr/bin/env python3
"""
Hardware Controller - Control Linux system hardware settings.

Capabilities:
  - Volume control (get/set/mute/unmute)
  - Screen brightness (get/set)
  - Power management (sleep, hibernate, shutdown, restart, lock)
  - Network adapters (list, enable, disable, WiFi scan, network info)
  - USB devices (list)

Requirements: Linux with standard tools (amixer, xbacklight/brillo, systemctl, ip, lsusb)
Dependencies: None
"""

import subprocess
import json
import sys
import os

if sys.stdout.encoding != 'utf-8':
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
if sys.stderr.encoding != 'utf-8':
    sys.stderr.reconfigure(encoding='utf-8', errors='replace')


def run_command(cmd, timeout=30):
    """Execute shell command with proper encoding."""
    try:
        result = subprocess.run(
            cmd,
            shell=True,
            capture_output=True,
            text=True,
            timeout=timeout,
            encoding="utf-8",
            errors="replace"
        )
        return result.stdout.strip(), result.stderr.strip(), result.returncode
    except subprocess.TimeoutExpired:
        return "", "ERROR: Command timed out", -1
    except Exception as e:
        return "", f"ERROR: {e}", -1


# ============ Volume Control ============

def volume_get():
    """Get current volume level."""
    stdout, stderr, code = run_command("amixer get Master")
    if code != 0:
        return {"error": f"Failed to get volume: {stderr}"}

    # Parse amixer output
    import re
    match = re.search(r'\[(\d+)%\]', stdout)
    if match:
        volume = int(match.group(1))
        muted = '[off]' in stdout
        return {"volume": volume, "muted": muted}
    return {"error": "Failed to parse volume"}


def volume_set(level):
    """Set volume level (0-100)."""
    if not 0 <= level <= 100:
        return {"error": "Volume must be between 0 and 100"}

    cmd = f"amixer set Master {level}%"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True, "volume": level}
    return {"error": f"Failed to set volume: {stderr}"}


def volume_mute():
    """Mute volume."""
    cmd = "amixer set Master mute"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True, "muted": True}
    return {"error": f"Failed to mute volume: {stderr}"}


def volume_unmute():
    """Unmute volume."""
    cmd = "amixer set Master unmute"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True, "muted": False}
    return {"error": f"Failed to unmute volume: {stderr}"}


# ============ Screen Brightness ============

def brightness_get():
    """Get current screen brightness level."""
    stdout, stderr, code = run_command("xbacklight -get")
    if code == 0:
        brightness = float(stdout)
        return {"brightness": round(brightness, 2)}
    return {"error": f"Failed to get brightness: {stderr}"}


def brightness_set(level):
    """Set screen brightness level (0-100)."""
    if not 0 <= level <= 100:
        return {"error": "Brightness must be between 0 and 100"}

    cmd = f"xbacklight -set {level}"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True, "brightness": level}
    return {"error": f"Failed to set brightness: {stderr}"}


# ============ Power Management ============

def power_lock():
    """Lock the screen."""
    cmd = "xdg-screensaver lock"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True}
    # Fallback: use dm-tool
    cmd2 = "dm-tool lock"
    stdout, stderr, code = run_command(cmd2)
    if code == 0:
        return {"success": True}
    return {"error": "Failed to lock screen"}


def power_sleep():
    """Put system to sleep."""
    cmd = "systemctl suspend"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True}
    return {"error": f"Failed to suspend: {stderr}"}


def power_hibernate():
    """Hibernate the system."""
    cmd = "systemctl hibernate"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True}
    return {"error": f"Failed to hibernate: {stderr}"}


def power_shutdown():
    """Shutdown the system."""
    cmd = "shutdown -h now"
    return {"success": True, "message": "System is shutting down"}


def power_restart():
    """Restart the system."""
    cmd = "shutdown -r now"
    return {"success": True, "message": "System is restarting"}


# ============ Network ============

def network_list_adapters():
    """List all network adapters."""
    cmd = "ip -o link show | awk '{print $2, $9}' | sed 's/:$//'"
    stdout, stderr, code = run_command(cmd)
    if code != 0:
        return {"error": f"Failed to list adapters: {stderr}"}

    adapters = []
    for line in stdout.split('\n'):
        if not line.strip():
            continue
        parts = line.split(None, 1)
        if len(parts) == 2:
            adapters.append({
                "name": parts[0],
                "status": parts[1]
            })
    return {"adapters": adapters}


def network_enable(adapter):
    """Enable a network adapter."""
    cmd = f"ip link set {adapter} up"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True, "adapter": adapter}
    return {"error": f"Failed to enable {adapter}: {stderr}"}


def network_disable(adapter):
    """Disable a network adapter."""
    cmd = f"ip link set {adapter} down"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True, "adapter": adapter}
    return {"error": f"Failed to disable {adapter}: {stderr}"}


def network_wifi_scan():
    """Scan for WiFi networks."""
    cmd = "nmcli device wifi list"
    stdout, stderr, code = run_command(cmd)
    if code != 0:
        return {"error": f"Failed to scan WiFi: {stderr}"}

    networks = []
    lines = stdout.split('\n')[1:]  # Skip header
    for line in lines:
        if not line.strip():
            continue
        parts = line.split()
        if len(parts) >= 8:
            networks.append({
                "ssid": parts[1],
                "mode": parts[2],
                "channel": parts[3],
                "rate": parts[4],
                "signal": parts[5],
                "bars": parts[6],
                "security": parts[7]
            })
    return {"networks": networks}


def network_info():
    """Get network information."""
    # IP addresses
    cmd = "ip addr show | grep 'inet ' | grep -v '127.0.0.1'"
    ip_stdout, _, _ = run_command(cmd)

    # Default gateway
    cmd2 = "ip route | grep default"
    gateway_stdout, _, _ = run_command(cmd2)

    return {
        "ip_addresses": ip_stdout.split('\n') if ip_stdout else [],
        "gateway": gateway_stdout if gateway_stdout else None
    }


# ============ USB ============

def usb_list():
    """List all USB devices."""
    cmd = "lsusb"
    stdout, stderr, code = run_command(cmd)
    if code != 0:
        return {"error": f"Failed to list USB devices: {stderr}"}

    devices = []
    for line in stdout.split('\n'):
        if not line.strip():
            continue
        # Parse: Bus 001 Device 002: ID 1234:5678 Manufacturer Name
        import re
        match = re.match(r'Bus (\d+) Device (\d+): ID ([\da-f]+):([\da-f]+) (.+)', line)
        if match:
            devices.append({
                "bus": int(match.group(1)),
                "device": int(match.group(2)),
                "vendor_id": match.group(3),
                "product_id": match.group(4),
                "name": match.group(5).strip()
            })
    return {"devices": devices}


def main():
    import argparse
    parser = argparse.ArgumentParser(description="Hardware Controller for Linux")
    subparsers = parser.add_subparsers(dest='category', help='Category to control')

    # Volume
    volume_parser = subparsers.add_parser('volume', help='Volume control')
    volume_subparsers = volume_parser.add_subparsers(dest='action', help='Volume action')
    volume_subparsers.add_parser('get', help='Get volume')
    volume_subparsers.add_parser('mute', help='Mute volume')
    volume_subparsers.add_parser('unmute', help='Unmute volume')
    volume_set_parser = volume_subparsers.add_parser('set', help='Set volume')
    volume_set_parser.add_argument('--level', type=int, required=True, help='Volume level (0-100)')

    # Screen
    screen_parser = subparsers.add_parser('screen', help='Screen control')
    screen_subparsers = screen_parser.add_subparsers(dest='action', help='Screen action')
    brightness_parser = screen_subparsers.add_parser('brightness', help='Brightness control')
    brightness_subparsers = brightness_parser.add_subparsers(dest='subaction', help='Brightness action')
    brightness_subparsers.add_parser('get', help='Get brightness')
    brightness_set_parser = brightness_subparsers.add_parser('set', help='Set brightness')
    brightness_set_parser.add_argument('--level', type=int, required=True, help='Brightness level (0-100)')

    # Power
    power_parser = subparsers.add_parser('power', help='Power management')
    power_subparsers = power_parser.add_subparsers(dest='action', help='Power action')
    power_subparsers.add_parser('lock', help='Lock screen')
    power_subparsers.add_parser('sleep', help='Suspend')
    power_subparsers.add_parser('hibernate', help='Hibernate')
    power_subparsers.add_parser('shutdown', help='Shutdown')
    power_subparsers.add_parser('restart', help='Restart')

    # Network
    network_parser = subparsers.add_parser('network', help='Network control')
    network_subparsers = network_parser.add_subparsers(dest='action', help='Network action')
    network_subparsers.add_parser('list', help='List adapters')
    network_subparsers.add_parser('wifi', help='Scan WiFi')
    network_subparsers.add_parser('info', help='Network info')
    network_enable_parser = network_subparsers.add_parser('enable', help='Enable adapter')
    network_enable_parser.add_argument('--adapter', required=True, help='Adapter name')
    network_disable_parser = network_subparsers.add_parser('disable', help='Disable adapter')
    network_disable_parser.add_argument('--adapter', required=True, help='Adapter name')

    # USB
    usb_parser = subparsers.add_parser('usb', help='USB control')
    usb_subparsers = usb_parser.add_subparsers(dest='action', help='USB action')
    usb_subparsers.add_parser('list', help='List USB devices')

    args = parser.parse_args()

    if not args.category:
        parser.print_help()
        sys.exit(1)

    result = {}

    # Volume
    if args.category == 'volume':
        if args.action == 'get':
            result = volume_get()
        elif args.action == 'set':
            result = volume_set(args.level)
        elif args.action == 'mute':
            result = volume_mute()
        elif args.action == 'unmute':
            result = volume_unmute()

    # Screen
    elif args.category == 'screen':
        if args.action == 'brightness':
            if args.subaction == 'get':
                result = brightness_get()
            elif args.subaction == 'set':
                result = brightness_set(args.level)

    # Power
    elif args.category == 'power':
        if args.action == 'lock':
            result = power_lock()
        elif args.action == 'sleep':
            result = power_sleep()
        elif args.action == 'hibernate':
            result = power_hibernate()
        elif args.action == 'shutdown':
            result = power_shutdown()
        elif args.action == 'restart':
            result = power_restart()

    # Network
    elif args.category == 'network':
        if args.action == 'list':
            result = network_list_adapters()
        elif args.action == 'enable':
            result = network_enable(args.adapter)
        elif args.action == 'disable':
            result = network_disable(args.adapter)
        elif args.action == 'wifi':
            result = network_wifi_scan()
        elif args.action == 'info':
            result = network_info()

    # USB
    elif args.category == 'usb':
        if args.action == 'list':
            result = usb_list()

    print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()


path: scripts/iot_controller.py
content: #!/usr/bin/env python3
"""
IoT Controller - Control smart home devices via APIs.

Supported platforms:
  - Home Assistant (REST API)
  - Mijia / XiaoMi (HTTP API, requires token)
  - Generic HTTP/REST endpoints

Requirements: requests (pip install requests)
Usage examples:
  python iot_controller.py homeassistant --url http://192.168.1.100:8123 --token YOUR_TOKEN --action list_entities
  python iot_controller.py homeassistant --url http://192.168.1.100:8123 --token YOUR_TOKEN --action call_service --domain light --service turn_on --entity_id light.living_room
  python iot_controller.py http --url http://192.168.1.50/api --action get --path /status
  python iot_controller.py http --url http://192.168.1.50/api --action post --path /command --body '{"command":"on"}'
"""

import sys
import json
import subprocess
import os

if sys.stdout.encoding != 'utf-8':
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
if sys.stderr.encoding != 'utf-8':
    sys.stderr.reconfigure(encoding='utf-8', errors='replace')


def check_requests():
    """Ensure requests library is available."""
    try:
        import requests
        return requests
    except ImportError:
        print("Installing requests...")
        subprocess.check_call(
            [sys.executable, "-m", "pip", "install", "requests", "-q"],
            stdout=subprocess.DEVNULL
        )
        import requests
        return requests


# ========== Home Assistant ==========

def ha_list_entities(base_url, token):
    """List all entities from Home Assistant."""
    requests = check_requests()
    url = f"{base_url.rstrip('/')}/api/states"
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    
    try:
        resp = requests.get(url, headers=headers, timeout=10)
        if resp.status_code == 200:
            entities = []
            for entity in resp.json():
                entities.append({
                    "entity_id": entity["entity_id"],
                    "state": entity["state"],
                    "friendly_name": entity["attributes"].get("friendly_name", ""),
                    "domain": entity["entity_id"].split(".")[0]
                })
            return json.dumps(entities, indent=2, ensure_ascii=False)
        else:
            return f"ERROR: HTTP {resp.status_code} - {resp.text}"
    except Exception as e:
        return f"ERROR: {e}"


def ha_get_state(base_url, token, entity_id):
    """Get state of a specific entity."""
    requests = check_requests()
    url = f"{base_url.rstrip('/')}/api/states/{entity_id}"
    headers = {"Authorization": f"Bearer {token}"}
    
    try:
        resp = requests.get(url, headers=headers, timeout=10)
        if resp.status_code == 200:
            return json.dumps(resp.json(), indent=2, ensure_ascii=False)
        else:
            return f"ERROR: HTTP {resp.status_code}"
    except Exception as e:
        return f"ERROR: {e}"


def ha_call_service(base_url, token, domain, service, entity_id, service_data=None):
    """Call a Home Assistant service."""
    requests = check_requests()
    url = f"{base_url.rstrip('/')}/api/services/{domain}/{service}"
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    
    payload = {"entity_id": entity_id}
    if service_data:
        if isinstance(service_data, str):
            try:
                payload.update(json.loads(service_data))
            except json.JSONDecodeError:
                return "ERROR: Invalid service_data JSON"
        else:
            payload.update(service_data)
    
    try:
        resp = requests.post(url, headers=headers, json=payload, timeout=10)
        if resp.status_code == 200:
            return f"OK: Called {domain}.{service} on {entity_id}"
        else:
            return f"ERROR: HTTP {resp.status_code} - {resp.text}"
    except Exception as e:
        return f"ERROR: {e}"


def ha_turn_on(base_url, token, entity_id, params=None):
    """Turn on an entity."""
    domain = entity_id.split(".")[0]
    return ha_call_service(base_url, token, domain, "turn_on", entity_id, params)


def ha_turn_off(base_url, token, entity_id):
    """Turn off an entity."""
    domain = entity_id.split(".")[0]
    return ha_call_service(base_url, token, domain, "turn_off", entity_id)


def ha_toggle(base_url, token, entity_id):
    """Toggle an entity."""
    domain = entity_id.split(".")[0]
    return ha_call_service(base_url, token, domain, "toggle", entity_id)


# ========== Generic HTTP ==========

def http_get(url, path="", headers=None):
    """Send HTTP GET request."""
    requests = check_requests()
    full_url = f"{url.rstrip('/')}/{path.lstrip('/')}" if path else url
    hdrs = {}
    if headers:
        for h in headers:
            k, v = h.split(":", 1)
            hdrs[k.strip()] = v.strip()
    
    try:
        resp = requests.get(full_url, headers=hdrs, timeout=10)
        try:
            body = resp.json()
            return json.dumps({"status": resp.status_code, "data": body}, indent=2, ensure_ascii=False)
        except Exception:
            return f"Status: {resp.status_code}\n{resp.text[:2000]}"
    except Exception as e:
        return f"ERROR: {e}"


def http_post(url, path="", body=None, headers=None):
    """Send HTTP POST request."""
    requests = check_requests()
    full_url = f"{url.rstrip('/')}/{path.lstrip('/')}" if path else url
    hdrs = {"Content-Type": "application/json"}
    if headers:
        for h in headers:
            k, v = h.split(":", 1)
            hdrs[k.strip()] = v.strip()
    
    payload = None
    if body:
        try:
            payload = json.loads(body) if isinstance(body, str) else body
        except json.JSONDecodeError:
            payload = body
    
    try:
        resp = requests.post(full_url, headers=hdrs, json=payload, timeout=10)
        try:
            rbody = resp.json()
            return json.dumps({"status": resp.status_code, "data": rbody}, indent=2, ensure_ascii=False)
        except Exception:
            return f"Status: {resp.status_code}\n{resp.text[:2000]}"
    except Exception as e:
        return f"ERROR: {e}"


def http_put(url, path="", body=None, headers=None):
    """Send HTTP PUT request."""
    requests = check_requests()
    full_url = f"{url.rstrip('/')}/{path.lstrip('/')}" if path else url
    hdrs = {"Content-Type": "application/json"}
    if headers:
        for h in headers:
            k, v = h.split(":", 1)
            hdrs[k.strip()] = v.strip()
    
    payload = None
    if body:
        try:
            payload = json.loads(body) if isinstance(body, str) else body
        except json.JSONDecodeError:
            payload = body
    
    try:
        resp = requests.put(full_url, headers=hdrs, json=payload, timeout=10)
        try:
            rbody = resp.json()
            return json.dumps({"status": resp.status_code, "data": rbody}, indent=2, ensure_ascii=False)
        except Exception:
            return f"Status: {resp.status_code}\n{resp.text[:2000]}"
    except Exception as e:
        return f"ERROR: {e}"


# ========== Mijia / XiaoMi ==========

def mijia_discover():
    """Discover Mijia devices on local network (basic SSDP scan)."""
    print("INFO: For Mijia device control, use the miio library:")
    print("  pip install miio")
    print("  python -m miio discover")
    return "INFO: Run the above commands to discover Mijia devices"


# ========== Main ==========

def main():
    import argparse
    parser = argparse.ArgumentParser(description="IoT Controller")
    sub = parser.add_subparsers(dest="platform")

    # Home Assistant
    p_ha = sub.add_parser("homeassistant", help="Home Assistant control")
    ha_sub = p_ha.add_subparsers(dest="action")
    ha_sub.add_parser("list", help="List all entities")
    
    ha_state = ha_sub.add_parser("state", help="Get entity state")
    ha_state.add_argument("--entity-id", type=str, required=True)
    
    ha_call = ha_sub.add_parser("call", help="Call service")
    ha_call.add_argument("--domain", type=str, required=True)
    ha_call.add_argument("--service", type=str, required=True)
    ha_call.add_argument("--entity-id", type=str, required=True)
    ha_call.add_argument("--data", type=str, help="JSON service data")
    
    ha_on = ha_sub.add_parser("on", help="Turn on entity")
    ha_on.add_argument("--entity-id", type=str, required=True)
    ha_on.add_argument("--data", type=str, help="JSON params (e.g. brightness)")
    
    ha_off = ha_sub.add_parser("off", help="Turn off entity")
    ha_off.add_argument("--entity-id", type=str, required=True)
    
    ha_tog = ha_sub.add_parser("toggle", help="Toggle entity")
    ha_tog.add_argument("--entity-id", type=str, required=True)
    
    p_ha.add_argument("--url", type=str, required=True, help="Home Assistant base URL")
    p_ha.add_argument("--token", type=str, required=True, help="Long-lived access token")

    # Generic HTTP
    p_http = sub.add_parser("http", help="Generic HTTP/REST control")
    http_sub = p_http.add_subparsers(dest="action")
    
    http_get_p = http_sub.add_parser("get", help="HTTP GET")
    http_get_p.add_argument("--path", type=str, default="")
    
    http_post_p = http_sub.add_parser("post", help="HTTP POST")
    http_post_p.add_argument("--path", type=str, default="")
    http_post_p.add_argument("--body", type=str, default=None)
    
    http_put_p = http_sub.add_parser("put", help="HTTP PUT")
    http_put_p.add_argument("--path", type=str, default="")
    http_put_p.add_argument("--body", type=str, default=None)
    
    p_http.add_argument("--url", type=str, required=True)
    p_http.add_argument("--header", type=str, action="append", help="Header in 'Key: Value' format")

    # Mijia
    p_mi = sub.add_parser("mijia", help="Mijia/XiaoMi control")
    mi_sub = p_mi.add_subparsers(dest="action")
    mi_sub.add_parser("discover", help="Discover devices")

    args = parser.parse_args()

    if args.platform == "homeassistant":
        url = args.url
        [REDACTED_SECRET]
        if args.action == "list":
            print(ha_list_entities(url, token))
        elif args.action == "state":
            print(ha_get_state(url, token, args.entity_id))
        elif args.action == "call":
            print(ha_call_service(url, token, args.domain, args.service, args.entity_id, args.data))
        elif args.action == "on":
            print(ha_turn_on(url, token, args.entity_id, args.data))
        elif args.action == "off":
            print(ha_turn_off(url, token, args.entity_id))
        elif args.action == "toggle":
            print(ha_toggle(url, token, args.entity_id))
        else:
            p_ha.print_help()
    elif args.platform == "http":
        if args.action == "get":
            print(http_get(args.url, args.path, args.header))
        elif args.action == "post":
            print(http_post(args.url, args.path, args.body, args.header))
        elif args.action == "put":
            print(http_put(args.url, args.path, args.body, args.header))
        else:
            p_http.print_help()
    elif args.platform == "mijia":
        if args.action == "discover":
            print(mijia_discover())
        else:
            p_mi.print_help()
    else:
        parser.print_help()


if __name__ == "__main__":
    main()


path: scripts/process_manager.py
content: #!/usr/bin/env python3
"""
Process Manager - List, start, stop, and monitor system processes on Linux.

Requirements: Linux with standard tools (ps, pgrep, pkill)
Dependencies: None
"""

import subprocess
import json
import sys
import os

if sys.stdout.encoding != 'utf-8':
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
if sys.stderr.encoding != 'utf-8':
    sys.stderr.reconfigure(encoding='utf-8', errors='replace')


def run_command(cmd, timeout=30):
    """Execute shell command with proper encoding."""
    try:
        result = subprocess.run(
            cmd,
            shell=True,
            capture_output=True,
            text=True,
            timeout=timeout,
            encoding="utf-8",
            errors="replace"
        )
        return result.stdout.strip(), result.stderr.strip(), result.returncode
    except subprocess.TimeoutExpired:
        return "", "ERROR: Command timed out", -1
    except Exception as e:
        return "", f"ERROR: {e}", -1


def list_processes(name=None):
    """List running processes. Optionally filter by name."""
    if name:
        cmd = f"pgrep -fl {name}"
        stdout, stderr, code = run_command(cmd)
        if code != 0:
            return {"error": f"No processes found with name: {name}", "processes": []}

        processes = []
        for line in stdout.split('\n'):
            if not line.strip():
                continue
            parts = line.split(None, 1)
            if len(parts) == 2:
                pid = parts[0]
                cmd_line = parts[1]
                processes.append({
                    "pid": int(pid),
                    "name": cmd_line.split()[0] if cmd_line else "",
                    "command": cmd_line
                })
        return {"processes": processes}
    else:
        cmd = "ps aux --sort=-%cpu | head -20"
        stdout, stderr, code = run_command(cmd)
        if code != 0:
            return {"error": f"Failed to list processes: {stderr}"}

        processes = []
        lines = stdout.split('\n')[1:]  # Skip header
        for line in lines:
            if not line.strip():
                continue
            parts = line.split(None, 10)
            if len(parts) >= 11:
                processes.append({
                    "user": parts[0],
                    "pid": int(parts[1]),
                    "cpu": float(parts[2]),
                    "mem": float(parts[3]),
                    "vsz": int(parts[4]),
                    "rss": int(parts[5]),
                    "tty": parts[6],
                    "stat": parts[7],
                    "start": parts[8],
                    "time": parts[9],
                    "command": parts[10] if len(parts) > 10 else ""
                })
        return {"processes": processes}


def kill_process(pid=None, name=None):
    """Kill a process by PID or name."""
    if name:
        cmd = f"pkill -f {name}"
    elif pid:
        cmd = f"kill {pid}"
    else:
        return {"error": "Either --pid or --name is required"}

    stdout, stderr, code = run_command(cmd)
    if code == 0:
        killed_by = name if name else pid
        return {"success": True, "killed": killed_by}
    else:
        return {"error": f"Failed to kill process: {stderr}"}


def start_process(command):
    """Start a process."""
    try:
        # Use nohup to run in background
        process = subprocess.Popen(
            command,
            shell=True,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            start_new_session=True
        )
        return {"success": True, "pid": process.pid, "command": command}
    except Exception as e:
        return {"error": f"Failed to start process: {e}"}


def get_process_info(pid):
    """Get detailed information about a process."""
    cmd = f"ps -p {pid} -o pid,ppid,user,%cpu,%mem,vsz,rss,tty,stat,start,time,comm --no-headers"
    stdout, stderr, code = run_command(cmd)
    if code != 0:
        return {"error": f"Process {pid} not found"}

    parts = stdout.split()
    return {
        "pid": int(parts[0]),
        "ppid": int(parts[1]),
        "user": parts[2],
        "cpu": float(parts[3]),
        "mem": float(parts[4]),
        "vsz": int(parts[5]),
        "rss": int(parts[6]),
        "tty": parts[7],
        "stat": parts[8],
        "start": parts[9],
        "time": parts[10],
        "command": parts[11] if len(parts) > 11 else ""
    }


def system_info():
    """Get overall system information."""
    # CPU info
    cpu_info_cmd = "nproc"
    cpu_count, _, _ = run_command(cpu_info_cmd)

    # Memory info
    mem_info_cmd = "free -h | grep Mem"
    mem_info, _, _ = run_command(mem_info_cmd)

    # Load average
    load_cmd = "uptime | awk -F'load average:' '{print $2}'"
    load_avg, _, _ = run_command(load_cmd)

    # Process count
    proc_cmd = "ps aux | wc -l"
    proc_count, _, _ = run_command(proc_cmd)

    return {
        "cpu_cores": int(cpu_count) if cpu_count else 0,
        "memory": mem_info,
        "load_average": load_avg,
        "process_count": int(proc_count) if proc_count else 0
    }


def main():
    import argparse
    parser = argparse.ArgumentParser(description="Process Manager for Linux")
    subparsers = parser.add_subparsers(dest='action', help='Action to perform')

    # List processes
    parser_list = subparsers.add_parser('list', help='List processes')
    parser_list.add_argument('--name', help='Filter by process name')

    # Kill process
    parser_kill = subparsers.add_parser('kill', help='Kill a process')
    parser_kill.add_argument('--pid', type=int, help='Process ID')
    parser_kill.add_argument('--name', help='Process name')

    # Start process
    parser_start = subparsers.add_parser('start', help='Start a process')
    parser_start.add_argument('command', help='Command to execute')

    # Get process info
    parser_info = subparsers.add_parser('info', help='Get process info')
    parser_info.add_argument('--pid', type=int, required=True, help='Process ID')

    # System info
    parser_system = subparsers.add_parser('system', help='Get system information')

    args = parser.parse_args()

    if not args.action:
        parser.print_help()
        sys.exit(1)

    result = {}
    if args.action == 'list':
        result = list_processes(name=args.name)
    elif args.action == 'kill':
        result = kill_process(pid=args.pid, name=args.name)
    elif args.action == 'start':
        result = start_process(args.command)
    elif args.action == 'info':
        result = get_process_info(args.pid)
    elif args.action == 'system':
        result = system_info()

    print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()


path: scripts/serial_comm.py
content: #!/usr/bin/env python3
"""
Serial Communication - Communicate with Arduino and other serial devices.

Capabilities:
  - List available serial ports
  - Connect to a serial device
  - Send data (text or bytes)
  - Receive data (with timeout)
  - Continuous monitor mode
  - Auto-detect common baud rates

Requirements: pyserial (pip install pyserial)
"""

import sys
import json
import time
import subprocess
import os

if sys.stdout.encoding != 'utf-8':
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
if sys.stderr.encoding != 'utf-8':
    sys.stderr.reconfigure(encoding='utf-8', errors='replace')


def check_pyserial():
    """Check if pyserial is installed, install if not."""
    try:
        import serial
        import serial.tools.list_ports
        return True
    except ImportError:
        print("Installing pyserial...")
        subprocess.check_call(
            [sys.executable, "-m", "pip", "install", "pyserial", "-q"],
            stdout=subprocess.DEVNULL
        )
        try:
            import serial
            return True
        except ImportError:
            return False


def list_ports():
    """List all available serial ports."""
    if not check_pyserial():
        return '{"error":"Failed to install pyserial"}'
    
    import serial.tools.list_ports
    
    ports = []
    for port in serial.tools.list_ports.comports():
        ports.append({
            "device": port.device,
            "description": port.description,
            "hwid": port.hwid,
            "vendor": port.vid if port.vid else None,
            "product": port.pid if port.pid else None,
        })
    
    if not ports:
        return json.dumps({"info": "No serial ports found"}, ensure_ascii=False)
    return json.dumps(ports, indent=2, ensure_ascii=False)


def detect_baud_rate(port, timeout=2):
    """Try to detect the baud rate by testing common rates."""
    if not check_pyserial():
        return "ERROR: pyserial not available"
    
    import serial
    
    common_rates = [9600, 115200, 57600, 38400, 19200, 4800, 2400, 1200]
    
    for rate in common_rates:
        try:
            ser = serial.Serial(port, rate, timeout=timeout)
            ser.write(b'\n')
            time.sleep(0.5)
            response = ser.read(ser.in_waiting or 1)
            ser.close()
            if response:
                return json.dumps({
                    "detected_rate": rate,
                    "response_preview": response.decode('utf-8', errors='replace')[:100]
                })
        except Exception:
            continue
    
    return json.dumps({"detected_rate": 9600, "note": "Could not auto-detect, using default 9600"})


def send_data(port, data, baud_rate=9600, encoding="utf-8", newline=True):
    """Send data to a serial port."""
    if not check_pyserial():
        return "ERROR: pyserial not available"
    
    import serial
    
    try:
        ser = serial.Serial(port, baud_rate, timeout=2)
        payload = (data + '\n').encode(encoding) if newline else data.encode(encoding)
        ser.write(payload)
        ser.flush()
        ser.close()
        return f"OK: Sent {len(payload)} bytes to {port} at {baud_rate} baud"
    except serial.SerialException as e:
        return f"ERROR: {e}"


def receive_data(port, baud_rate=9600, timeout=2, max_bytes=1024, encoding="utf-8"):
    """Receive data from a serial port."""
    if not check_pyserial():
        return "ERROR: pyserial not available"
    
    import serial
    
    try:
        ser = serial.Serial(port, baud_rate, timeout=timeout)
        data = ser.read(max_bytes)
        ser.close()
        
        if data:
            try:
                text = data.decode(encoding)
            except UnicodeDecodeError:
                text = data.hex()
            return json.dumps({
                "bytes_received": len(data),
                "data": text,
                "hex": data.hex()[:200]
            }, ensure_ascii=False)
        else:
            return '{"info":"No data received within timeout"}'
    except serial.SerialException as e:
        return f"ERROR: {e}"


def send_and_receive(port, data, baud_rate=9600, timeout=2, encoding="utf-8"):
    """Send data and wait for response."""
    if not check_pyserial():
        return "ERROR: pyserial not available"
    
    import serial
    
    try:
        ser = serial.Serial(port, baud_rate, timeout=timeout)
        ser.write((data + '\n').encode(encoding))
        ser.flush()
        time.sleep(0.1)
        response = ser.read(ser.in_waiting or 1024)
        ser.close()
        
        try:
            text = response.decode(encoding)
        except UnicodeDecodeError:
            text = response.hex()
        
        return json.dumps({
            "sent": data,
            "received": text,
            "bytes": len(response)
        }, ensure_ascii=False)
    except serial.SerialException as e:
        return f"ERROR: {e}"


def monitor(port, baud_rate=9600, duration=10, encoding="utf-8"):
    """Monitor serial port output for a duration (outputs in real-time)."""
    if not check_pyserial():
        return "ERROR: pyserial not available"
    
    import serial
    
    try:
        ser = serial.Serial(port, baud_rate, timeout=0.1)
        print(f"Monitoring {port} at {baud_rate} baud for {duration}s...")
        print("--- Press Ctrl+C to stop ---")
        
        start = time.time()
        buffer = b""
        while time.time() - start < duration:
            if ser.in_waiting:
                chunk = ser.read(ser.in_waiting)
                buffer += chunk
                try:
                    print(chunk.decode(encoding), end="", flush=True)
                except UnicodeDecodeError:
                    print(chunk.hex(), flush=True)
            time.sleep(0.05)
        
        ser.close()
        print(f"\n--- Monitor ended. Total bytes: {len(buffer)} ---")
        return ""
    except KeyboardInterrupt:
        ser.close()
        print("\n--- Monitor stopped by user ---")
        return ""
    except serial.SerialException as e:
        return f"ERROR: {e}"


def main():
    import argparse
    parser = argparse.ArgumentParser(description="Serial Communication")
    sub = parser.add_subparsers(dest="action")

    p_list = sub.add_parser("list", help="List serial ports")
    
    p_detect = sub.add_parser("detect", help="Detect baud rate")
    p_detect.add_argument("--port", type=str, required=True)
    
    p_send = sub.add_parser("send", help="Send data")
    p_send.add_argument("--port", type=str, required=True)
    p_send.add_argument("--data", type=str, required=True)
    p_send.add_argument("--baud", type=int, default=9600)
    p_send.add_argument("--no-newline", action="store_true")
    
    p_recv = sub.add_parser("receive", help="Receive data")
    p_recv.add_argument("--port", type=str, required=True)
    p_recv.add_argument("--baud", type=int, default=9600)
    p_recv.add_argument("--timeout", type=float, default=2)
    
    p_chat = sub.add_parser("chat", help="Send and receive")
    p_chat.add_argument("--port", type=str, required=True)
    p_chat.add_argument("--data", type=str, required=True)
    p_chat.add_argument("--baud", type=int, default=9600)
    
    p_mon = sub.add_parser("monitor", help="Monitor port")
    p_mon.add_argument("--port", type=str, required=True)
    p_mon.add_argument("--baud", type=int, default=9600)
    p_mon.add_argument("--duration", type=int, default=10)

    args = parser.parse_args()

    if args.action == "list":
        print(list_ports())
    elif args.action == "detect":
        print(detect_baud_rate(args.port))
    elif args.action == "send":
        print(send_data(args.port, args.data, args.baud, newline=not args.no_newline))
    elif args.action == "receive":
        print(receive_data(args.port, args.baud, args.timeout))
    elif args.action == "chat":
        print(send_and_receive(args.port, args.data, args.baud))
    elif args.action == "monitor":
        monitor(args.port, args.baud, args.duration)
    else:
        parser.print_help()


if __name__ == "__main__":
    main()


path: scripts/window_manager.py
content: #!/usr/bin/env python3
"""
Window Manager - Linux desktop application control via xdotool and wmctrl.

Requirements: Linux with X11/Wayland, xdotool, wmctrl
Dependencies: None (uses system tools)
"""

import subprocess
import json
import sys
import os

# Fix print encoding
if sys.stdout.encoding != 'utf-8':
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
if sys.stderr.encoding != 'utf-8':
    sys.stderr.reconfigure(encoding='utf-8', errors='replace')


def run_command(cmd, timeout=30):
    """Execute shell command with proper encoding handling."""
    try:
        result = subprocess.run(
            cmd,
            shell=True,
            capture_output=True,
            text=True,
            timeout=timeout,
            encoding="utf-8",
            errors="replace"
        )
        return result.stdout.strip(), result.stderr.strip(), result.returncode
    except subprocess.TimeoutExpired:
        return "", "ERROR: Command timed out", -1
    except Exception as e:
        return "", f"ERROR: {e}", -1


def list_windows():
    """List all visible windows with title, process name, and window ID."""
    stdout, stderr, code = run_command("wmctrl -l")
    if code != 0:
        return {"error": f"Failed to list windows: {stderr}"}

    windows = []
    for line in stdout.split('\n'):
        if not line.strip():
            continue
        parts = line.split(None, 3)  # Split into max 4 parts
        if len(parts) >= 4:
            window_id = parts[0]
            desktop = parts[1]
            pid = parts[2]
            title = parts[3] if len(parts) > 3 else ""
            windows.append({
                "window_id": window_id,
                "desktop": desktop,
                "pid": int(pid) if pid.isdigit() else 0,
                "title": title
            })

    return {"windows": windows}


def activate_window(window_id=None, title=None, pid=None):
    """Activate (bring to foreground) a window."""
    if not window_id:
        # Find window by title or pid
        result = list_windows()
        if "error" in result:
            return result

        for win in result["windows"]:
            if title and title.lower() in win["title"].lower():
                window_id = win["window_id"]
                break
            if pid and win["pid"] == pid:
                window_id = win["window_id"]
                break

    if not window_id:
        return {"error": "Window not found"}

    stdout, stderr, code = run_command(f"wmctrl -ia {window_id}")
    if code == 0:
        return {"success": True, "window_id": window_id}
    else:
        return {"error": f"Failed to activate window: {stderr}"}


def close_window(window_id=None, title=None, pid=None):
    """Close a window."""
    if not window_id:
        result = list_windows()
        if "error" in result:
            return result

        for win in result["windows"]:
            if title and title.lower() in win["title"].lower():
                window_id = win["window_id"]
                break
            if pid and win["pid"] == pid:
                window_id = win["window_id"]
                break

    if not window_id:
        return {"error": "Window not found"}

    stdout, stderr, code = run_command(f"wmctrl -ic {window_id}")
    if code == 0:
        return {"success": True, "window_id": window_id}
    else:
        return {"error": f"Failed to close window: {stderr}"}


def minimize_window(window_id=None, title=None, pid=None):
    """Minimize a window."""
    if not window_id:
        result = list_windows()
        if "error" in result:
            return result

        for win in result["windows"]:
            if title and title.lower() in win["title"].lower():
                window_id = win["window_id"]
                break
            if pid and win["pid"] == pid:
                window_id = win["window_id"]
                break

    if not window_id:
        return {"error": "Window not found"}

    stdout, stderr, code = run_command(f"xdotool windowminimize {window_id}")
    if code == 0:
        return {"success": True, "window_id": window_id}
    else:
        return {"error": f"Failed to minimize window: {stderr}"}


def maximize_window(window_id=None, title=None, pid=None):
    """Maximize a window."""
    if not window_id:
        result = list_windows()
        if "error" in result:
            return result

        for win in result["windows"]:
            if title and title.lower() in win["title"].lower():
                window_id = win["window_id"]
                break
            if pid and win["pid"] == pid:
                window_id = win["window_id"]
                break

    if not window_id:
        return {"error": "Window not found"}

    stdout, stderr, code = run_command(f"wmctrl -ir {window_id} -b add,maximized_vert,maximized_horz")
    if code == 0:
        return {"success": True, "window_id": window_id}
    else:
        return {"error": f"Failed to maximize window: {stderr}"}


def resize_window(window_id=None, title=None, pid=None, x=None, y=None, width=None, height=None):
    """Resize and move a window."""
    if not window_id:
        result = list_windows()
        if "error" in result:
            return result

        for win in result["windows"]:
            if title and title.lower() in win["title"].lower():
                window_id = win["window_id"]
                break
            if pid and win["pid"] == pid:
                window_id = win["window_id"]
                break

    if not window_id:
        return {"error": "Window not found"}

    # Format: gravity, X, Y, width, height
    # Using 0 for gravity (NorthWest)
    cmd = f"wmctrl -ir {window_id} -e 0,{x},{y},{width},{height}"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True, "window_id": window_id}
    else:
        return {"error": f"Failed to resize window: {stderr}"}


def move_window(window_id=None, title=None, pid=None, x=None, y=None):
    """Move a window to specified position."""
    if not window_id:
        result = list_windows()
        if "error" in result:
            return result

        for win in result["windows"]:
            if title and title.lower() in win["title"].lower():
                window_id = win["window_id"]
                break
            if pid and win["pid"] == pid:
                window_id = win["window_id"]
                break

    if not window_id:
        return {"error": "Window not found"}

    # Get current window geometry
    stdout, stderr, code = run_command(f"xdotool getwindowgeometry {window_id}")
    if code != 0:
        return {"error": f"Failed to get window geometry: {stderr}"}

    # Parse current geometry to get width and height
    import re
    geometry = re.search(r'Geometry: (\d+)x(\d+)', stdout)
    if not geometry:
        return {"error": "Failed to parse window geometry"}

    width = geometry.group(1)
    height = geometry.group(2)

    # Move and resize to preserve dimensions
    cmd = f"wmctrl -ir {window_id} -e 0,{x},{y},{width},{height}"
    stdout, stderr, code = run_command(cmd)
    if code == 0:
        return {"success": True, "window_id": window_id}
    else:
        return {"error": f"Failed to move window: {stderr}"}


def send_keys(window_id=None, title=None, pid=None, text=None):
    """Send keystrokes to a window."""
    if not window_id:
        result = list_windows()
        if "error" in result:
            return result

        for win in result["windows"]:
            if title and title.lower() in win["title"].lower():
                window_id = win["window_id"]
                break
            if pid and win["pid"] == pid:
                window_id = win["window_id"]
                break

    if not window_id:
        return {"error": "Window not found"}

    # Activate window first
    activate_window(window_id=window_id)

    # Type the text (xdotool uses --delay to avoid issues with special chars)
    escaped_text = text.replace("'", "'\\''")
    stdout, stderr, code = run_command(f"xdotool type --delay 10 '{escaped_text}'")
    if code == 0:
        return {"success": True, "window_id": window_id}
    else:
        return {"error": f"Failed to send keys: {stderr}"}


def main():
    import argparse
    parser = argparse.ArgumentParser(description="Window Manager for Linux")
    subparsers = parser.add_subparsers(dest='action', help='Action to perform')

    # List windows
    parser_list = subparsers.add_parser('list', help='List all windows')

    # Activate window
    parser_activate = subparsers.add_parser('activate', help='Activate a window')
    parser_activate.add_argument('--title', help='Window title to search')
    parser_activate.add_argument('--pid', type=int, help='Process ID')
    parser_activate.add_argument('--window-id', help='Window ID')

    # Close window
    parser_close = subparsers.add_parser('close', help='Close a window')
    parser_close.add_argument('--title', help='Window title to search')
    parser_close.add_argument('--pid', type=int, help='Process ID')
    parser_close.add_argument('--window-id', help='Window ID')

    # Minimize window
    parser_minimize = subparsers.add_parser('minimize', help='Minimize a window')
    parser_minimize.add_argument('--title', help='Window title to search')
    parser_minimize.add_argument('--pid', type=int, help='Process ID')
    parser_minimize.add_argument('--window-id', help='Window ID')

    # Maximize window
    parser_maximize = subparsers.add_parser('maximize', help='Maximize a window')
    parser_maximize.add_argument('--title', help='Window title to search')
    parser_maximize.add_argument('--pid', type=int, help='Process ID')
    parser_maximize.add_argument('--window-id', help='Window ID')

    # Resize window
    parser_resize = subparsers.add_parser('resize', help='Resize a window')
    parser_resize.add_argument('--title', help='Window title to search')
    parser_resize.add_argument('--pid', type=int, help='Process ID')
    parser_resize.add_argument('--window-id', help='Window ID')
    parser_resize.add_argument('--x', type=int, required=True, help='X position')
    parser_resize.add_argument('--y', type=int, required=True, help='Y position')
    parser_resize.add_argument('--width', type=int, required=True, help='Width')
    parser_resize.add_argument('--height', type=int, required=True, help='Height')

    # Move window
    parser_move = subparsers.add_parser('move', help='Move a window')
    parser_move.add_argument('--title', help='Window title to search')
    parser_move.add_argument('--pid', type=int, help='Process ID')
    parser_move.add_argument('--window-id', help='Window ID')
    parser_move.add_argument('--x', type=int, required=True, help='X position')
    parser_move.add_argument('--y', type=int, required=True, help='Y position')

    # Send keys
    parser_send_keys = subparsers.add_parser('send-keys', help='Send keystrokes to window')
    parser_send_keys.add_argument('--title', help='Window title to search')
    parser_send_keys.add_argument('--pid', type=int, help='Process ID')
    parser_send_keys.add_argument('--window-id', help='Window ID')
    parser_send_keys.add_argument('--text', required=True, help='Text to type')

    args = parser.parse_args()

    if not args.action:
        parser.print_help()
        sys.exit(1)

    result = {}
    if args.action == 'list':
        result = list_windows()
    elif args.action == 'activate':
        result = activate_window(window_id=args.window_id, title=args.title, pid=args.pid)
    elif args.action == 'close':
        result = close_window(window_id=args.window_id, title=args.title, pid=args.pid)
    elif args.action == 'minimize':
        result = minimize_window(window_id=args.window_id, title=args.title, pid=args.pid)
    elif args.action == 'maximize':
        result = maximize_window(window_id=args.window_id, title=args.title, pid=args.pid)
    elif args.action == 'resize':
        result = resize_window(window_id=args.window_id, title=args.title, pid=args.pid,
                              x=args.x, y=args.y, width=args.width, height=args.height)
    elif args.action == 'move':
        result = move_window(window_id=args.window_id, title=args.title, pid=args.pid,
                            x=args.x, y=args.y)
    elif args.action == 'send-keys':
        result = send_keys(window_id=args.window_id, title=args.title, pid=args.pid, text=args.text)

    print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()


path: references/command_reference.md
content: # Linux System Controller - Command Reference

## Script Locations

All scripts are located at: `scripts/`

---

## 1. window_manager.py - Window Management

### List all visible windows
```bash
python3 scripts/window_manager.py list
```
Returns JSON array of windows with window_id, title, pid, and desktop.

### Activate (bring to foreground)
```bash
python3 scripts/window_manager.py activate --title "Terminal"
python3 scripts/window_manager.py activate --window-id 0x1400001
python3 scripts/window_manager.py activate --pid 1234
```

### Close a window
```bash
python3 scripts/window_manager.py close --title "Untitled - Terminal"
python3 scripts/window_manager.py close --window-id 0x1400001
```

### Minimize / Maximize
```bash
python3 scripts/window_manager.py minimize --title "Terminal"
python3 scripts/window_manager.py maximize --window-id 0x1400001
```

### Resize and move
```bash
python3 scripts/window_manager.py resize --window-id 0x1400001 --x 100 --y 100 --width 800 --height 600
```

### Move a window
```bash
python3 scripts/window_manager.py move --title "Terminal" --x 200 --y 200
```

### Send keystrokes to window
```bash
python3 scripts/window_manager.py send-keys --title "Terminal" --text "Hello World"
```

---

## 2. process_manager.py - Process Management

### List all processes (top 20 by CPU)
```bash
python3 scripts/process_manager.py list
```

### List processes by name
```bash
python3 scripts/process_manager.py list --name chrome
```

### Kill a process
```bash
python3 scripts/process_manager.py kill --pid 1234
python3 scripts/process_manager.py kill --name firefox
```

### Start a process
```bash
python3 scripts/process_manager.py start "gedit"
python3 scripts/process_manager.py start "firefox"
```

### Get process info
```bash
python3 scripts/process_manager.py info --pid 1234
```

### Get system information
```bash
python3 scripts/process_manager.py system
```

---

## 3. hardware_controller.py - Hardware Control

### Volume control

#### Get current volume
```bash
python3 scripts/hardware_controller.py volume get
```

#### Set volume (0-100)
```bash
python3 scripts/hardware_controller.py volume set --level 50
```

#### Mute/Unmute
```bash
python3 scripts/hardware_controller.py volume mute
python3 scripts/hardware_controller.py volume unmute
```

### Screen brightness

#### Get brightness
```bash
python3 scripts/hardware_controller.py screen brightness get
```

#### Set brightness (0-100)
```bash
python3 scripts/hardware_controller.py screen brightness set --level 70
```

### Power management

#### Lock screen
```bash
python3 scripts/hardware_controller.py power lock
```

#### Sleep (suspend)
```bash
python3 scripts/hardware_controller.py power sleep
```

#### Hibernate
```bash
python3 scripts/hardware_controller.py power hibernate
```

#### Shutdown
```bash
python3 scripts/hardware_controller.py power shutdown
```

#### Restart
```bash
python3 scripts/hardware_controller.py power restart
```

### Network

#### List network adapters
```bash
python3 scripts/hardware_controller.py network list
```

#### Enable adapter
```bash
python3 scripts/hardware_controller.py network enable --adapter eth0
```

#### Disable adapter
```bash
python3 scripts/hardware_controller.py network disable --adapter eth0
```

#### Scan WiFi networks
```bash
python3 scripts/hardware_controller.py network wifi
```

#### Get network info
```bash
python3 scripts/hardware_controller.py network info
```

### USB

#### List USB devices
```bash
python3 scripts/hardware_controller.py usb list
```

---

## 4. serial_comm.py - Serial Communication

### List available serial ports
```bash
python3 scripts/serial_comm.py list
```
Returns JSON array of available serial ports.

### Send data to port
```bash
python3 scripts/serial_comm.py send --port /dev/ttyUSB0 --data "LED_ON"
```

### Receive data from port
```bash
python3 scripts/serial_comm.py receive --port /dev/ttyUSB0 --timeout 5
```

### Chat mode (send and receive)
```bash
python3 scripts/serial_comm.py chat --port /dev/ttyUSB0 --data "GET_TEMP"
```

### Monitor port continuously
```bash
python3 scripts/serial_comm.py monitor --port /dev/ttyUSB0 --duration 30
```

---

## 5. iot_controller.py - IoT Controller

### Home Assistant

#### List all entities
```bash
python3 scripts/iot_controller.py homeassistant --url http://192.168.1.100:8123 --token YOUR_TOKEN list
```

#### Get entity state
```bash
python3 scripts/iot_controller.py homeassistant --url http://192.168.1.100:8123 --token YOUR_TOKEN state --entity-id light.living_room
```

#### Turn on device
```bash
python3 scripts/iot_controller.py homeassistant --url http://192.168.1.100:8123 --token YOUR_TOKEN on --entity-id light.living_room
```

#### Turn off device
```bash
python3 scripts/iot_controller.py homeassistant --url http://192.168.1.100:8123 --token YOUR_TOKEN off --entity-id light.bedroom
```

#### Toggle device
```bash
python3 scripts/iot_controller.py homeassistant --url http://192.168.1.100:8123 --token YOUR_TOKEN toggle --entity-id switch.kitchen
```

#### Call custom service
```bash
python3 scripts/iot_controller.py homeassistant --url http://192.168.1.100:8123 --token YOUR_TOKEN call_service --domain light --service turn_on --service_data '{"brightness": 255}'
```

### Generic HTTP

#### GET request
```bash
python3 scripts/iot_controller.py http --url http://192.168.1.50/api get --path /status
```

#### POST request
```bash
python3 scripts/iot_controller.py http --url http://192.168.1.50/api post --path /command --body '{"command":"on"}'
```

#### PUT request
```bash
python3 scripts/iot_controller.py http --url http://192.168.1.50/api put --path /config --body '{"mode":"auto"}'
```

#### With custom headers
```bash
python3 scripts/iot_controller.py http --url http://192.168.1.50/api get --path /data --headers '{"Authorization":"Bearer token123"}'
```

---

## 6. gui_controller.py - GUI Control

### Mouse

#### Get mouse position
```bash
python3 scripts/gui_controller.py mouse position
```

#### Move mouse
```bash
python3 scripts/gui_controller.py mouse move --x 500 --y 300
```

#### Click
```bash
python3 scripts/gui_controller.py mouse click --x 500 --y 300
python3 scripts/gui_controller.py mouse click  # Click at current position
```

#### Right click
```bash
python3 scripts/gui_controller.py mouse right-click --x 500 --y 300
```

#### Double click
```bash
python3 scripts/gui_controller.py mouse double-click --x 500 --y 300
```

#### Drag
```bash
python3 scripts/gui_controller.py mouse drag --start-x 100 --start-y 200 --end-x 500 --end-y 400
```

#### Scroll
```bash
python3 scripts/gui_controller.py mouse scroll --direction down --clicks 10
python3 scripts/gui_controller.py mouse scroll --direction up --clicks 5
```

### Keyboard

#### Type text
```bash
python3 scripts/gui_controller.py keyboard type --text "Hello World"
```

#### Press hotkeys
```bash
python3 scripts/gui_controller.py keyboard press --keys "ctrl+c"
python3 scripts/gui_controller.py keyboard press --keys "alt+tab"
python3 scripts/gui_controller.py keyboard press --keys "ctrl+shift+t"
```

### Screenshot

#### Full screen
```bash
python3 scripts/gui_controller.py screenshot full
```

#### Region
```bash
python3 scripts/gui_controller.py screenshot region --x 100 --y 100 --width 800 --height 600
```

#### Get screen size
```bash
python3 scripts/gui_controller.py screenshot size
```

### Visual (OCR and Image Recognition)

#### OCR full screen
```bash
python3 scripts/gui_controller.py visual ocr
```

#### OCR region
```bash
python3 scripts/gui_controller.py visual ocr --x 100 --y 100 --width 800 --height 600
```

#### Find template on screen
```bash
python3 scripts/gui_controller.py visual find --template button.png
```

#### Click image template
```bash
python3 scripts/gui_controller.py visual click-image --template submit.png
```

#### Get pixel color
```bash
python3 scripts/gui_controller.py visual pixel --x 200 --y 200
```

---

## Notes

- All commands return JSON output for easy parsing
- Use `python3` to run scripts (Python 3.6+ required)
- Some commands require additional system tools:
  - `xdotool`, `wmctrl` for window management
  - `amixer`, `xbacklight` for hardware control
  - `tesseract-ocr` for OCR functionality
- Install system tools with: `apt-get install xdotool wmctrl tesseract-ocr alsa-utils`
- Python dependencies are auto-installed on first use


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)