惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
V
V2EX
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Martin Fowler
Martin Fowler
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
小众软件
小众软件
Vercel News
Vercel News
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
V
Visual Studio Blog
J
Java Code Geeks
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
B
Blog
美团技术团队
量子位

博客园 - SKILL·NULL

获取指定 dom 的 touchmove 方向及到达边缘时是否禁止橡皮筋效果 如何实现超长数字:先缩小字号,再省略号 如何通过传参来实现SVG排序图标的三种状态 如何为GIT设置全局勾子,为每次提交追加信息 一文了解大模型、小模型与各类神经网络的关系 Karabiner-Elements最常用配置 IndexedDB封装 echarts获取坐标上的点距离顶部底部高度 Let`s Encrypt 生成免费自动续签 HTTPS 证书 H5滚动截取长图 ReactNative常见问题及处理 根据.nvmrc自动切换项目所需node版本 Command PhaseScriptExecution failed with a nonzero exit code echarts双Y轴,实现均分为包含刻度0的指定段数,同时对齐刻度 env(safe-area-inset-bottom) 兼容写法 缩放实现0.5px 禁止 IOS 橡皮筋效果 JS 拦截浏览器返回 海康威视DS-IPC-E42H-IWPT监控画面竖线处理 Echarts 5 动态按需引入图表 React 18 自定义 Hook 获取 useState 最新值 处理报错 ResizeObserver loop completed with undelivered notifications.
如何在Mac上调整外星人鼠标AW720M的灯光颜色
SKILL·NULL · 2026-02-25 · via 博客园 - SKILL·NULL

OpenRGB可以调整部分外星人外设的灯光,目前不支持AW720M,在Mac上又不能安装AWCC,这一点戴尔做的属实垃圾,目前亲测成功的是,使用 Hidapi 向外设发送指令来更改灯光颜色。

整体流程:

# AW720M 灯光控制脚本(Mac)

通过 HID Feature Report 控制 Alienware AW720M 等设备的 RGB 灯光。

## 遇到 externally-managed-environment 时(Homebrew Python)

系统禁止直接 `pip3 install` 时,可用下面任一方式,**无需手动建虚拟环境**。

### 方式一:pipx run(推荐,零安装)

每次用 `pipx run` 自动带好 hidapi,不用激活 venv、不用改系统 Python:

```bash
cd Hidapi
# 首次使用需安装 pipx:brew install pipx && pipx ensurepath
pipx run --spec hidapi python3 AW720M.py --list
pipx run --spec hidapi python3 AW720M.py --r 255 --g 0 --b 0
```

### 方式二:允许 pip 安装到系统(一次安装,之后直接 python3)

接受 PEP 668 的覆盖方式,以后可直接 `python3 AW720M.py`:

```bash
pip3 install --break-system-packages hidapi
cd Hidapi
python3 AW720M.py --list
python3 AW720M.py --r 255 --g 0 --b 0
```

### 方式三:虚拟环境(传统做法)

```bash
cd Hidapi
python3 -m venv .venv
source .venv/bin/activate
pip install hidapi
python3 AW720M.py --list
python3 AW720M.py --r 255 --g 0 --b 0
```

## 使用

```bash
# 枚举本机 HID 设备,确认鼠标的 VID/PID
python3 AW720M.py --list

# 设置灯光颜色(不写死 VID/PID,按设备名称自动发现;或 --list 后用 --vid/--pid 指定)
python3 AW720M.py --r 255 --g 0 --b 0
python3 AW720M.py --r 0 --g 255 --b 0
python3 AW720M.py --r 0 --g 0 --b 255
```

(若用方式一,上述命令前加:`pipx run --spec hidapi`。)

## 提示「无法打开设备 / open failed」时

- 脚本**不写死 VID/PID**,设备 ID 从枚举结果动态获取;名称含 Alienware/AW720 等即会匹配。
1. **macOS 输入监控权限**:系统设置 → 隐私与安全性 → 输入监控 → 勾选「终端」(或你运行脚本的 App)。
2. **用 sudo 试一次**:`sudo python3 AW720M.py --r 255 --g 0 --b 0`(若这样能成功,多半是权限问题)。
3. 关闭 **Alienware Command Center、Dell 外设管理** 等可能占用 HID 的软件后再试。
4. **AW720M 请用 USB 线连接**(不要仅用蓝牙)再运行脚本。
5. **macOS 限制**:系统可能独占鼠标的 HID 接口,导致即使用 sudo 仍「open failed」。此时只能在 Windows 或 Linux 下用本脚本控制灯光。
6. 脚本会遍历所有匹配接口并尝试按 path / 按 vid:pid 打开;若仍失败,可用 `--list` 查看本机 VID:PID 后以 `--vid/--pid` 明确指定。

## 参数说明

| 参数 | 说明 |
|------|------|
| `--list` | 列出所有 HID 设备(用于查看 VID/PID) |
| `--vid 0xXXXX` | USB Vendor ID(不指定则按设备名称自动发现) |
| `--pid 0xXXXX` | USB Product ID(不指定则按设备名称自动发现) |
| `--r`, `--g`, `--b` | 红/绿/蓝 0–255 |
| `--zone N` | 灯区索引,默认 0 |

协议参考 OpenRGB 的 Alienware 控制器实现。

Python3 脚本:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AW720M 灯光控制(Mac,可不使用虚拟环境)
通过 HID Feature Report 设置 Alienware 鼠标/设备的 RGB 灯光。
依赖:pip3 install --user hidapi  或  brew install hidapi 后 pip 安装 hidapi
"""

from __future__ import print_function

import argparse
import sys
import time

try:
    import hid
except ImportError:
    print("请先安装 hidapi:", file=sys.stderr)
    print("  方式一(推荐,无需改系统):pipx run --spec hidapi python3 AW720M.py --list", file=sys.stderr)
    print("  方式二(一次安装):pip3 install --break-system-packages hidapi", file=sys.stderr)
    print("  方式三:python3 -m venv .venv && source .venv/bin/activate && pip install hidapi", file=sys.stderr)
    sys.exit(1)

# 协议常量
# APIv4:34 字节 Feature Report(OpenRGB Alienware 笔记本/台式)
REPORT_SIZE = 33
HIDAPI_REPORT_SIZE = REPORT_SIZE + 1
CMD_SET_COLOR = 0x27
# APIv7:65 字节 USB Interrupt OUT(Alienware 鼠标,如 AW610M/AW720M,见 alienfx-tools)
APIv7_REPORT_SIZE = 65
APIv7_CMD_CONTROL = (0x40, 0x10, 0x0c, 0x00, 0x01)  # COMMV7_control
APIv7_EFFECT_COLOR = 1  # v7OpCodes[AlienFX_A_Color]
# COMMV7_update:设色后发送以“提交”,否则后续设色可能不生效
APIv7_CMD_UPDATE = (0x40, 0x60, 0x07, 0x00, 0xc0, 0x4e, 0x00, 0x01)

# 自动发现时仅按设备名称匹配,VID/PID 完全从枚举结果动态获取,不写死
NAME_KEYWORDS = ("alienware", "aw720", "g series")


def _path_str(d):
    p = d.get("path")
    if p is None:
        return ""
    return p.decode("utf-8", errors="replace") if isinstance(p, bytes) else p


def _str_lower(d, key):
    s = (d.get(key) or "").strip() or ""
    if isinstance(s, bytes):
        s = s.decode("utf-8", errors="replace")
    return s.lower()


def _device_name_matches(d, keywords=NAME_KEYWORDS):
    """设备厂商或产品名包含任一关键词即视为匹配,用于动态发现。"""
    product = _str_lower(d, "product_string")
    manufacturer = _str_lower(d, "manufacturer_string")
    combined = product + " " + manufacturer
    return any(kw in combined for kw in keywords)


def list_hid_devices():
    """枚举所有 HID 设备,便于确认 AW720M 的 VID/PID。"""
    print("HID 设备列表(VID:PID 路径 厂商 产品):")
    print("-" * 60)
    for d in hid.enumerate():
        print("  {:04x}:{:04x}  {}  {}  {}".format(
            d["vendor_id"],
            d["product_id"],
            _path_str(d),
            (d.get("manufacturer_string") or "").strip() or "-",
            (d.get("product_string") or "").strip() or "-",
        ))
    print("-" * 60)
    print("未指定 --vid/--pid 时会按设备名称自动匹配(名称含 Alienware/AW720 等);VID:PID 从枚举动态获取。")


def find_target_devices(vid=None, pid=None, name_keywords=None):
    """
    动态查找目标设备,不写死 VID/PID。
    - 若指定了 vid+pid:枚举该 VID:PID 下所有 HID 接口,返回 [(path, vid, pid), ...]。
    - 未指定:枚举全部设备,按厂商/产品名包含关键词匹配,返回匹配项的 path 与枚举得到的 vid/pid。
    返回 [(path, vid, pid), ...],vid/pid 均来自枚举结果。
    """
    out = []
    for d in hid.enumerate(vid if vid is not None else 0, pid if pid is not None else 0):
        v, p = d["vendor_id"], d["product_id"]
        if vid is not None and pid is not None:
            if v != vid or p != pid:
                continue
        else:
            if not _device_name_matches(d, name_keywords or NAME_KEYWORDS):
                continue
        out.append((d["path"], v, p))
    return out


def send_feature_report(dev, buf):
    """发送 HID Feature Report,并做短暂延时避免设备忙。"""
    dev.send_feature_report(bytes(buf))
    time.sleep(0.06)


def get_feature_report(dev, report_id=0x00, size=HIDAPI_REPORT_SIZE):
    """读取 HID Feature Report。"""
    return dev.get_feature_report(report_id, size)


def set_color_apiv7(dev, r, g, b, zone=0, brightness=0x64, retries=5):
    """
    APIv7:65 字节 USB Interrupt OUT,用于 Alienware 鼠标(AW610M/AW720M 等)。
    先发 control 设色,再发 update 提交;重复 retries 次并加延时,提高“有时有效有时无效”时的成功率。
    """
    # 预建 control 与 update 包,避免重复逻辑
    buf = bytearray(APIv7_REPORT_SIZE)
    buf[0] = 0x00
    for i, byte_val in enumerate(APIv7_CMD_CONTROL):
        buf[1 + i] = byte_val
    buf[5] = APIv7_EFFECT_COLOR
    buf[6] = brightness & 0xFF
    buf[7] = zone & 0xFF
    buf[8] = r & 0xFF
    buf[9] = g & 0xFF
    buf[10] = b & 0xFF
    control_packet = bytes(buf)

    upd = bytearray(APIv7_REPORT_SIZE)
    upd[0] = 0x00
    for i, byte_val in enumerate(APIv7_CMD_UPDATE):
        upd[1 + i] = byte_val
    update_packet = bytes(upd)

    time.sleep(0.05)  # 打开设备后稍等再发,避免首包被丢
    for _ in range(retries):
        dev.write(control_packet)
        time.sleep(0.12)
        dev.write(update_packet)
        time.sleep(0.12)


def set_color_direct(dev, r, g, b, zones=(0,)):
    """
    APIv4:34 字节 HID Feature Report,用于部分 Alienware 笔记本/台式。
    部分设备不回复读取,发送后若读回失败则忽略。
    """
    buf = bytearray(HIDAPI_REPORT_SIZE)
    buf[0] = 0x00
    buf[1] = 0x03
    buf[2] = CMD_SET_COLOR
    buf[3] = r & 0xFF
    buf[4] = g & 0xFF
    buf[5] = b & 0xFF
    n = len(zones)
    buf[6] = (n >> 8) & 0xFF
    buf[7] = n & 0xFF
    for i, z in enumerate(zones):
        if 8 + i < len(buf):
            buf[8 + i] = z & 0xFF
    send_feature_report(dev, buf)
    try:
        get_feature_report(dev)
    except OSError:
        pass


def open_device(vid, pid, path=None):
    """
    打开 HID 设备,兼容 hid.Device(新)与 hid.device()(旧)两种 API。
    path 优先;否则用 vid/pid。
    """
    def open_by_path(p):
        if hasattr(hid, "Device"):
            return hid.Device(path=p)
        dev = hid.device()
        dev.open_path(p)
        return dev

    def open_by_vid_pid(v, p):
        if hasattr(hid, "Device"):
            return hid.Device(vid=v, pid=p)
        dev = hid.device()
        dev.open(v, p)
        return dev

    if path is not None:
        return open_by_path(path)
    return open_by_vid_pid(vid, pid)


def main():
    ap = argparse.ArgumentParser(description="AW720M / Alienware HID 灯光控制(可不使用虚拟环境)")
    ap.add_argument("--list", action="store_true", help="枚举 HID 设备并退出,用于查看 VID/PID")
    ap.add_argument("--vid", type=lambda x: int(x, 0), default=None, help="USB Vendor ID(不指定则按设备名称自动发现)")
    ap.add_argument("--pid", type=lambda x: int(x, 0), default=None, help="USB Product ID(不指定则按设备名称自动发现)")
    ap.add_argument("--no-report-id", action="store_true", help="部分设备不需要 Report ID,当前实现仍带 1 字节")
    ap.add_argument("--r", type=int, default=None, metavar="0-255", help="红色")
    ap.add_argument("--g", type=int, default=None, metavar="0-255", help="绿色")
    ap.add_argument("--b", type=int, default=None, metavar="0-255", help="蓝色")
    ap.add_argument("--zone", type=int, default=0, help="灯区索引,默认 0")
    args = ap.parse_args()

    if args.list:
        list_hid_devices()
        return

    r = args.r if args.r is not None else 255
    g = args.g if args.g is not None else 0
    b = args.b if args.b is not None else 0
    r = max(0, min(255, r))
    g = max(0, min(255, g))
    b = max(0, min(255, b))

    # 动态查找目标设备:未指定 vid/pid 时按名称匹配,vid/pid 一律从枚举结果取
    candidates = find_target_devices(args.vid, args.pid)
    if not candidates:
        # 仅当用户同时指定了 vid 和 pid 时,再试一次直接按该 ID 打开
        if args.vid is not None and args.pid is not None:
            candidates = [(None, args.vid, args.pid)]
        else:
            print("未找到名称匹配的设备(Alienware/AW720 等)。请运行 python3 AW720M.py --list 查看设备列表,并用 --vid/--pid 指定。", file=sys.stderr)
            sys.exit(2)

    # 对每个候选接口都打开并发送设色命令(不只看第一个能打开的),避免枚举顺序导致有时命中 LED 有时没命中
    last_error = None
    any_open = False
    any_sent = False
    for path, vid_open, pid_open in candidates:
        dev = None
        try:
            dev = open_device(vid_open, pid_open, path=path)
            any_open = True
        except Exception as e:
            last_error = e
            continue
        try:
            try:
                set_color_apiv7(dev, r, g, b, zone=args.zone)
                any_sent = True
            except OSError:
                try:
                    set_color_direct(dev, r, g, b, zones=(args.zone,))
                    any_sent = True
                except OSError:
                    pass
        finally:
            if dev is not None:
                dev.close()

    # 若按 path 一个都没打开,再试一次仅用 vid/pid 打开
    if not any_open and candidates:
        vid_open, pid_open = candidates[0][1], candidates[0][2]
        try:
            dev = open_device(vid_open, pid_open, path=None)
            any_open = True
            try:
                set_color_apiv7(dev, r, g, b, zone=args.zone)
                any_sent = True
            except OSError:
                try:
                    set_color_direct(dev, r, g, b, zones=(args.zone,))
                    any_sent = True
                except OSError:
                    pass
            finally:
                dev.close()
        except Exception as e:
            last_error = e

    if not any_open:
        print("无法打开 Alienware 设备:{}".format(last_error), file=sys.stderr)
        print("", file=sys.stderr)
        print("常见原因与处理:", file=sys.stderr)
        print("  1. macOS 权限:系统设置 → 隐私与安全性 → 输入监控 → 勾选「终端」或你使用的 App。", file=sys.stderr)
        print("  2. 尝试用 sudo 运行:sudo python3 AW720M.py --r 255 --g 0 --b 0", file=sys.stderr)
        print("  3. 关闭 Alienware Command Center、Dell 外设管理等可能占用设备的软件后重试。", file=sys.stderr)
        print("  4. AW720M 请用 USB 线连接(不要仅用蓝牙),再试。", file=sys.stderr)
        print("  5. macOS 可能独占鼠标的 HID 接口,导致无法打开;可尝试在 Windows 或 Linux 下用本脚本。", file=sys.stderr)
        if not candidates or candidates[0][0] is None:
            print("  6. 运行 python3 AW720M.py --list 查看本机 HID 设备,用 --vid/--pid 指定你的设备。", file=sys.stderr)
        sys.exit(2)

    if any_sent:
        print("已设置灯光 R={} G={} B={}".format(r, g, b))
    else:
        print("已向设备各接口发送命令但均写失败;若灯光无变化可尝试关闭 Dell/Alienware 驱动或换 USB 口。", file=sys.stderr)


if __name__ == "__main__":
    main()

使用:

sudo python3 AW720M.py --r 255 --g 255 --b 255
来更改RGB颜色

Github:整外星人AW720M鼠标颜色