k0rventen/avea

GitHub: k0rventen/avea

一个用于通过Python和蓝牙低功耗协议控制Elgato Avea智能灯泡,并详细记录了其通信协议逆向工程过程的轻量级库。

Stars: 8 | Forks: 1

# 使用 Python 控制 Elgato Avea 灯泡 [![PyPI](https://img.shields.io/pypi/v/avea.svg)](https://pypi.org/project/avea/) [![构建与发布](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/022f5e719a205027.svg)](https://github.com/k0rventen/avea/actions/workflows/python-publish.yml) [Elgato 的 Avea 灯泡](https://www.amazon.co.uk/Elgato-Avea-Dynamic-Light-Android-Smartphone/dp/B00O4EZ11Q)是一款通过蓝牙连接到 iPhone 或 Android 应用程序的灯泡。 本项目旨在使用兼容蓝牙 4.0 的设备和一些 Python 魔法来控制它。 已在 Raspberry Pi 3 和 Zero W(带有内置蓝牙)上测试通过。 - [使用 Python 控制 Elgato Avea 灯泡](#control-of-an-elgato-avea-bulb-using-python) - [太长不看](#tldr) - [库的用法](#library-usage) - [代码文档](#code-documentation) - [灯泡的逆向工程](#reverse-engineering-of-the-bulb) - [通信协议](#communication-protocol) - [简介](#intro) - [命令与负载说明](#commands-and-payload-explanation) - [颜色命令](#color-command) - [亮度命令](#brightness-command) - [演练与示例](#walkthrough--example) - [亮度](#brightness) - [颜色](#color) - [Python 实现](#python-implementation) - [用于颜色计算的单行代码](#one-liner-for-color-computation) - [Bleak write\_gatt\_char 用法](#bleak-write_gatt_char-usage) - [使用 Bleak 处理通知](#working-with-notifications-using-bleak) - [待办事项](#todo) ## 太长不看 该库需要 [bleak](https://github.com/hbldh/bleak),因此无论我们是使用 pip 还是从源码安装,都必须安装以下依赖。 **依赖项** 确保系统蓝牙协议栈可用(例如 Linux 上的 `bluez` 或 macOS 上内置的 CoreBluetooth 框架)。使用 pip 安装时,Python 依赖 `bleak` 会自动安装。 **然后通过 pip3 安装** ``` sudo apt install python3-pip sudo pip3 install --upgrade avea ``` **或者,如果你更喜欢从源码安装** ``` git clone https://github.com/k0rventen/avea cd avea sudo python3 setup.py install ``` ## 库的用法 你可以查看示例脚本 `example.py`,直接在你的灯泡上运行尝试: ``` sudo python3 example.py ``` 下面是该库各种方法的快速入门指南。 **注意:根据你的操作系统配置,蓝牙发现可能需要额外的权限(例如在 Linux 上使用 sudo 运行,或者在 macOS 上授予蓝牙访问权限)。** ``` import avea # Important ! # 在列表中获取附近的 bulbs,然后检索所有 bulbs 的名称 # 根据平台的不同,discovery 可能需要提升的权限 nearbyBulbs = avea.discover_avea_bulbs() for bulb in nearbyBulbs: bulb.get_name() print(bulb.name) # 或者如果您知道其地址(例如在 scan 之后),则可以创建一个 bulb myBulb = avea.Bulb("xx:xx:xx:xx:xx:xx") # 您可以设置 brightness、color 和 name myBulb.set_brightness(2000) # ranges from 0 to 4095 myBulb.set_color(0,4095,0,0) # in order : white, red, green, blue myBulb.set_rgb(0,255,0) # RGB compliant function myBulb.set_smooth_transition(255,255,0,4,30) # change to rgb(255,255,0) in 4s with 30 iterations per second myBulb.set_name("bedroom") # new name of the bulb # 并获取 brightness、color 和 name print(myBulb.get_name()) # Query the name of the bulb theColor = myBulb.get_color() # Query the current color theRgbColor = myBulb.get_rgb() # Query the bulb in a RGB format theBrightness = myBulb.get_brightness() # query the current brightness theAddr = myBulb.addr # query the bulb Bluetooth addr theFwVersion = myBulb.get_fw_version() # query the bulb firmware version ``` 就是这样。非常简单。 查看下面的解释以获取更多信息,或者直接查看源码! ## 代码文档 ## 灯泡的逆向工程 我使用了 [Marmelatze](https://github.com/Marmelatze/avea_bulb) 提供的信息,以及通过 Android 设备上的 `btsnoop_hci.log` 文件和 Wireshark 进行的一些逆向工程。 下面是对 BLE 通信和用于与灯泡通信的 Python 实现的相当详尽的解释。 由于 BLE 通信相当复杂,如果你只是想使用该库,可以跳过所有这些内容。但这非常有趣。 ## 通信协议 ### 简介 灯泡使用蓝牙 4.0 "BLE" 进行通信,它为通信提供了一些有趣的特性,想了解更多关于它的信息请点击[这里](https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt)。 总而言之,灯泡会发出一组具有 `characteristics` 的 `services`。我们使用后者与设备进行通信。 灯泡使用服务 `f815e810456c6761746f4d756e696368` 和相关的特征 `f815e811456c6761746f4d756e696368` 来发送和接收关于其状态(颜色、名称和亮度)的信息。我们将通过该特征进行传输。 ### 命令与负载说明 传输的第一个字节是命令。可用的命令有: Value | Command --- | --- 0x35 | 设置 / 获取灯泡颜色 0x57 | 设置 / 获取灯泡亮度 0x58 | 设置 / 获取灯泡名称 ### 颜色命令 对于颜色命令,传输负载如下: Command | Fading time | Useless byte | White value | Red value | Green value | Blue value ---|---|---|---|---|---|--- 负载中的每个值都是一个 4 位十六进制值。(实际值为 0 到 4095 之间的整数) 对于每种颜色,需要在十六进制值中添加前缀: Color | prefix ---|--- White| 0x8000 Red | 0x3000 Green | 0x2000 Blue | 0X1000 然后,这些值将被格式化为**大端序**格式: Int | 4-bytes Hexadecimal | Big-endian hex ---|---|--- 4095 | 0x0fff| **0xff0f** ### 亮度命令 亮度同样是一个 0 到 4095 之间的整数值,以大端序的 4 字节十六进制值形式发送。传输内容如下所示: Command | Brightness value | ---|--- 0x57 | 0xff00 ## 演练与示例 假设我们希望灯泡在 75% 亮度下显示粉色: ### 亮度 75% 的亮度大约是 3072(满值为 4095): Int | 4-bytes Hexadecimal | **Big-endian hex** ---|---|--- 3072 |0x0C00| **0x000C** 亮度命令将是 `0x57000C` #### 颜色 粉色是 100% 红色、100% 蓝色,没有绿色。(我们假设白色值也为 0。)对于每种颜色,我们将整数值转换为十六进制,然后应用前缀,最后转换为大端序: Variables | Int Values | Hexadecimal values | Bitwise XOR | Big-endian values ---|---|---|---|--- White| 0| 0x0000| 0x8000| 0x0080 Red | 4095| 0x0fff| 0x3fff| 0xff3f Green | 0 | 0x0000| 0x2000 | 0x0020 Blue | 4095| 0x0fff | 0x1fff| 0xff1f 粉色灯泡的最终字节序列将是: Command | Fading time | Useless byte | White value | Red value | Green value | Blue value ---|---|---|---|---|---|--- `0x35`|`1101`| `0000`| `0080`|`ff3f`|`0020`|`ff1f` ## Python 实现 下面是一些关于各个非常有趣的方面的 python3 代码。 ### 用于颜色计算的单行代码 为了计算每种颜色的正确值,我创建了以下转换方法(此处以白色为例): ``` white = (int() | int(0x8000)).to_bytes(2, byteorder='little').hex() ``` ### Bleak write_gatt_char 用法 Bleak 允许我们将原始二进制负载直接发送到特征而无需任何额外重写。该库现在将负载准备为字节并直接调用客户端: ``` await client.write_gatt_char(CONTROL_CHARACTERISTIC_UUID, payload, response=False) ``` ### 使用 Bleak 处理通知 通知通过 `BleakClient.start_notify` 启用。在连接阶段,库会订阅 Avea 控制特征,并将每个通知路由到更新缓存灯泡状态的回调函数中。同步 getter 会等待一个 `asyncio.Event`,当接收到预期命令时会设置该事件,从而在底层利用 bleak 的同时保持公共 API 处于阻塞状态。 ## 待办事项 - 对 `ambiances`(基于情绪的场景)进行逆向工程。
标签:BLE, bleak, Bluetooth 4.0, bluez, CoreBluetooth, Elgato Avea, IoT, Python, Python3, Raspberry Pi, 云资产清单, 内存执行, 开源库, 搜索引擎爬虫, 无后门, 智能家居, 智能家居自动化, 智能灯泡控制, 物联网, 硬件 hacking, 硬件接口, 蓝牙低功耗, 蓝牙控制, 边缘计算, 逆向工具, 逆向工程, 通信协议