
用户反编译被搞坏的智能吊扇,实现本地控制
来源标题:I bricked my smart ceiling fan, then reverse-engineered it back to life
一名用户因不满智能吊扇无法设置通电即开,自行逆向工程实现完整本地控制,同时保留家人仍在用的原厂 App。起因是他不想额外买智能开关,而吊扇缺少其他智能设备常见的“记住上次状态”开关选项。他先尝试用 Home Assistant 自动化弥补,但 Wi-Fi 连接太慢,最终转向逆向工程。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
彻底实现本地控制一台云端风扇——同时不破坏家里其他人还在用的 App。
事情的起因,和我做的大多数事情一样,是因为懒。
最近我买了一台智能吊扇,这样躺在床上就不用起身,直接开关灯和风扇。用了几个星期后,我开始对它总是记住上一次状态这一点感到烦躁:如果我上次把它关掉了,这次通电后它还是关着的,我就得伸手去拿手机或者喊语音助手来打开,而这两样东西并不总在手边。自然的解决办法是再买一个智能开关来搭配风扇,但我不想这么做——墙上本来就已经有一个完全好用的开关,它就该直接起作用。我其他一些智能设备有个设置项,可以让你决定它们是记住上次状态,还是每次通电都自动开启。我的风扇没有这个设置。
几周后,我凑合出了一个偷懒的折中方案:一个 Home Assistant 自动化,在风扇通电的瞬间把灯打开。这确实能用,但从风扇通电到完全连上 Wi-Fi 并接入我的 Home Assistant,要等很久。我想要更快的方式,于是决定不再等厂商,自己来修开机行为。
我的第一反应是折腾固件,试着找一个现成的 cloudcutter 项目来复用。我找到了 dreo-cloudcutter——但它针对的是另一款 Dreo 产品,塔扇,内部结构跟我的不一样。所以它并不适用,不过是个有用的参考。两个产品的固件刷写方式非常相似:在配对模式下,Wi-Fi 模块会暴露一个无需认证的 /ota 上传页面。
我犯了一个错误:让我的 agent 去研究现有的 dreo-cloudcutter 项目怎样才能用在我的吊扇上,结果它决定往风扇里刷一个空的 ESPHome 镜像。
固件确实刷进去了,ESPHome 落到了 Wi-Fi 模块上——那是一颗 Beken BK7231N,负责联网的小芯片,而真正驱动风扇的是另一个独立的电机控制器。而这一“成功”毁掉了一切。现在 ESPHome 挂在我的网络上,完全可达、能登录,却毫无用处。我开不了风扇,开不了灯。Dreo App 里它显示为已消失,挂在 Dreo 账号下的 Google Home 控制也随之失效。我把一台好用的智能风扇,变成了一台带网页的“哑”风扇。
唯一值得庆幸的是,物理遥控器还能用。我实际上等于花大价钱买了一台带遥控器的风扇。
我当时还不知道、而后来拯救了整个项目的一点是:刷 ESPHome 只会覆盖应用固件。出厂数据存放在芯片上另一个独立区域,完全没有被碰过。
在对我 agent 发了几次全大写的催促、并意识到无法回滚之后,我决定向前推进,开始在网上寻找我这款风扇的固件。没有能用的东西。唯一的公开固件镜像是 2021 年的,而且还是错的型号——又是那款塔扇。
于是我转而研究 Dreo 手机 App。这个 App 显然有固件更新功能,说明它知道怎么获取固件,所以我让 agent 拆开 APK(com.hesung.dreo)并解码它的 React-Native bundle。收获很大:云 API 端点、完整的配对和注册流程、App 拉取所用的 S3 bucket,甚至证书配置。但它没有找到固件本身,只找到了让风扇自行更新的方法。我拿到了云端的地图,却没有回去的路。
坐在一间没有风扇的黑屋子里(只是为了戏剧效果,遥控器其实还能用),我决定已经没什么可失去的了。我让 agent 把它从 2021 年塔扇固件和反编译的 Android App 中学到的一切都用上,试着直接跟风扇的电机控制器——也就是第二颗芯片——对话,它用一套简短的 55AA 串口协议与 Wi-Fi 模块通信。
agent 开始做实验,很长时间里毫无反应。然后风扇“啾”了一声,正是你用遥控器改设置时它发出的那声小提示音。风扇的设置其实什么都没变,但它是在确认收到了信号。芯片在听;我只需要找到正确的“话”——以及后来才明白的,正确的“时机”。模块会走一套启动流程(几秒钟的静默,然后是一串心跳和状态帧),走完之后才会应用任何一次写入;在那之前,命令都会被无声吞掉。在一台死掉的风扇和一个死掉的 App 之后,那声提示音仍然是我几天来听到的最令人振奋的声音。
带着“这个思路也许可行”的动力,我搭了一个小工具,可以发送许多不同的命令,并通过读回数据来观察结果。
协议本身——55 AA——相当简单:一个命令字节、一条数据点(DP)记录,以及一个校验和。下面这个构造器标注了每个数据点最终被确认的用途;而在这个阶段,我只知道帧的结构,还不知道哪个 DP 是干什么的。你可以试试:
首先,我按遥控器上的按钮,记录 MCU 状态如何变化。这帮我读出了当前状态,但我没法把它翻译成真正能改变风扇状态的命令。
我的下一次尝试是架一台摄像头,正对着风扇朝上拍。agent 逐帧对比原始像素,弄清楚了“开”和“关”分别长什么样。有了反馈,我让 agent 基于它掌握的历史数据和对协议本身的理解,暴力枚举命令。
agent 不止一次自信地宣布电机“锁死”了,但每次都错——有一次所谓的“修复”,其实是我在背景里切换了一盏房间的灯,污染了画面。它还总是把灯和风扇的命令搞混(dp2 与 dp3)。它没理解控制器只对值的“变化”做出反应。等这些坑都弄明白之后,命令开始一条接一条地被攻破。
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。