记一次将智能家居网关接入 HomeAssistant

最近搬家,开发商的精装包括一套全屋智能设备,但是只能用开发商的App接入,这对我来说非常不能忍——控制自己家里的设备还要走一道互联网?所以我搬进来的 Day 1 就开始研究怎么把这些灯和空调之类的接到 HomeAssistant 里。最后前天晚上无意间发现的一个后台命令执行,终于打通了整个环节,让我得以通过自定义集成的方式将他接入内网的 HA。接下来就是整个过程。出于规避法律风险的考虑,具体型号和部分细节将在此略过,不影响理解,大家可以自行探索。
在开始之前,我也尝试过在网络上搜索方案,不过这套方案似乎是完全 to B 的,网络上资料稀少。搜索开发商名字在抖音上倒是有一些结果,但是他们的方案是非常昂贵、麻烦而且具有侵入性(需要拆机接线)的,需要自己配KNX/IP Gateway。所以我一直想,能不能利用现有的设备直接接入HA。
0x01 外围探索
首先观察这套全屋智能的控制设备,分别是客厅的一个主控面板,各个客房的分控面板,以及实体开关。主控面板根据观察是运行的 Android
系统设置中,网关连接、设备配置等不少选项都加密了,但是有一个拉起原生Settings的连接WiFi选项。从这里可以连接自己的WiFi,后续还发现这个面板默认5555端口开放,adb shell连上就是 root。但是最核心的这个面板本体APK被360加固了,拆壳过于麻烦,所以我先暂时放下,去观察其他设备。
接下来来到弱电箱,其中的核心设备就是题图里的这个主机。
根据官网的描述,它通过KNX、RS485等总线协议控制灯、空调等设备,同时还具有丰富的对接和云控功能。在这里,我们主要关注的是他的这个RJ45网口。
0x02 智能主机
在主机最顶部左侧找到RJ45网口,并将其连接到我们的局域网中,通过DHCP Leases分配可以看到他自动获取了IP。我们直接对这个主机进行端口扫描,可以发现开放了22、80等端口。现在打开浏览器访问其80端口,就是这个主机的后台管理系统。
通过观察 POST /login 这个请求以及一些简单的 F12 操作,我们来到了后台。
在后台我们可以配置各个区域以及下面的设备、场景、规则等
我们已经取得了一些进展,但目前仅仅是这样还不够。因为从前端分析,没有发现给我们任何控制这些设备的接口。接下来,让我们继续深入。
0x03 深入底层
在这个面板的参数设置界面,有一个“启动本地KNX调试”的按钮。我们点击一下并观察其请求,让人感到十分甚至九分的亲切,仿佛已经到了家目录一样。
系统里的curl可以用,也有完整bash。但是最简单的办法就是读一下 /etc/shadow
然后拿着密码连上 ssh。
观察 netstat 可以发现这个主机的 Web 服务就是由 SmartHome-Controller-1.0-SNAPSHOT-fat.jar
提供的,让我们读一下它的代码。
搜索相关关键字可以找到,/ntesec/controller/web/handler/v01
里面的代码就是这些 API 的处理逻辑,对我们有用的主要是这两个方法:
getRouter().route().path("/getDeviceStatus").method(HttpMethod.GET).handler(CtrlHomeREST::getDeviceStatus);
getRouter().route().path("/sendKNX").method(HttpMethod.GET).handler(CtrlHomeREST::sendKNX);
private static void sendKNX(RoutingContext routingContext) {
String realAddr = routingContext.request().getParam("realAddr");
String value = routingContext.request().getParam("value");
String length = routingContext.request().getParam("length");
EventBus eb = routingContext.vertx().eventBus();
JsonObject jsonObject = new JsonObject();
jsonObject.put("realAddr", realAddr);
jsonObject.put("value", value);
jsonObject.put("length", Integer.valueOf(length));
eb.send("ntesec.controller.executor.writeRealAddrKNX", jsonObject);
}
private static void getDeviceStatus(RoutingContext routingContext) {
String deviceIds = routingContext.request().getParam("deviceIds");
log.info("收到查询设备当前状态请求:deviceIds=》" + deviceIds);
Vertx vertx = routingContext.vertx();
EventBus eb = vertx.eventBus();
VertxFormLoginHandlerImpl.isOnline = true;
if (StringUtil.isNullOrEmpty(deviceIds)) {
log.info("返回当前所有设备状态" + GlobalData.statusMap);
RespUtil.writeJsonLmx("success", "", GlobalData.statusMap, routingContext);
} else {
String[] ids = deviceIds.split(",");
Map<Integer, JsonObject> tmp = new HashMap<>();
for (String id : ids) {
tmp.put(Integer.valueOf(id), (JsonObject)GlobalData.statusMap.get(Integer.valueOf(id)));
}
log.info("返回查询设备状态" + tmp);
RespUtil.writeJsonLmx("success", "", tmp, routingContext);
}
}
有了这些方法,就可以写入KNX总线信息和读取状态,足以实现一个 HomeAssistant Custom Component 了。
我:真是一点鉴权没做啊
群友:这个叫做方便用户二次开发
0x04 实现集成
接下来的工作由 Cursor 和 Claude 酱完成~ 我们只需要把接口定义和数据结构告诉她,然后她就会给我们写出一个自定义集成了。当然还需要一些调试,但是 Cursor 真的百倍地提升了工作效率,只花了两个小时就全部搞定了。
通过 HA 的集成能力,我们可以把这些设备接入到 HomeKit 或者小爱同学里面,也可以继续购买新的设备与它们联动,无限拓宽了这个原本封闭的开发商智能家居生态。
47:你群都是日日再用,不日能用?