0%

能不能只有我在家的时候空调才自动开

背景

小米空调伴侣可以控制家里的空调。通过米家 APP 开关和设置,也可以在上面设置定时。在南方湿冷的冬天,早上起床前自动把房间的空调开起来,床再也不会赖着我了。有很长的一段时间,我是这么用的。但是有几次出门,完全不记得空调会自动开。于是回家后发现空调一直开着…。于是就有了这篇博客的内容。能不能只有我在家的时候空调才自动开?

思路和实现

研究了一下,米家 APP 原生提供的功能似乎无法满足这个需求。不通过米家 APP 能不能做到呢?把这个拆分为两个问题:

  1. 怎么控制空调?
  2. 如何判断我在不在家?

如何控制空调

首先想到的是通过小爱同学,可以录好“小爱同学,开空调”,“小爱同学,关空调”这类音频。通过播放音频触发小爱同学来控制。也可以写一段代码来给空调伴侣发送开机命令。考虑到语音控制的准确性,另外突然播放一段音频可能也挺奇怪的。还是决定通过代码来控制。最后选择用 python-miio 来实现。

想要通过 miIO 协议来控制米家设备,需要先获取到设备的 ip 和 token。这个脚本可以从小米服务获取账号下所有设备的 token 等信息。

1
2
3
4
5
6
7
8
9
10
$ curl -LO https://github.com/PiotrMachowski/Xiaomi-cloud-tokens-extractor/raw/master/token_extractor.py
$ pip install pycrypto
...
$ python token_extractor.py
Username (email or user ID):
<your user ID>
Password:
<your password>
Server (one of: cn, de, us, ru, tw, sg, in, i2) Leave empty to check all available:
cn

获取到空调伴侣的 ip 和 token 之后,就可以通过 miIO 来控制设备(见文末代码)。

如何判断我在不在家

最简单的办法可能就是通过判断自己的手机有没有连接家里的 WI-FI。想到可以 ping 手机的 IP,最后看退出状态码是不是 0。

1
$ ping -c 3 192.168.2.146

-c 参数指定发送 n 个包以后退出。

最终这个效果不是特别好,手机会休眠,休眠以后 ping 可能会失败。特别是早上还没醒,手机可能处于待机状态。ping 可能会失败。既然 ping 不行,就通过路由器的 API 来查询连接的设备吧。不同路由器的接口不同,以家里的小米路由器为例,刷了 openwrt 系统。通过浏览器的开发者工具,来查看路由管理页面的网络请求信息。发现 openwrt 通过一个 /cgi-bin/luci 接口查询设备信息,也包括已经连接到路由器的设备。拿到连接设备列表以后,根据手机的 MAC 地址判断是否在连接设备列表中,就可以知道我是不是在家里了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def get_devices():
'''获取在线设备 MAC 列表'''
data = {
'luci_username': router_username,
'luci_password': router_password,
}
resp = requests.post("http://192.168.2.1/cgi-bin/luci", data=data, allow_redirects=False)
if resp.status_code != 302:
raise Exception('login error: {}, {}'.format(resp.status_code, resp.content))

data = [{
'jsonrpc': "2.0",
'method': "call",
'params': [resp.cookies.get('sysauth'), "iwinfo", "assoclist", {'device': "wlan1"}]
}]
resp = requests.post("http://192.168.2.1/ubus", json=data)
if resp.status_code != 200:
raise Exception('get ubus error %s %s' % (resp.status_code, resp.content))
return list(i.get('mac') for i in resp.json()[0].get('result')[1].get('results'))

定时任务

完成脚本以后,在家里的服务器上,配置一个 cron job。每天早上 06:30 尝试开启空调。同样脚本也实现了当我不在家才关空调的功能。设置一个每天上午 10:00 尝试关空调的任务。

1
2
30 06 * * * /home/eirture/scripts/acpartner/main.py on
00 10 * * * /home/eirture/scripts/acpartner/main.py off

总结

最终通过 miIO 协议发送命令给小米空调伴侣来控制空调。通过查询路由器已连接设备列表中是否有我的手机,来判断我是否在家。cron job 定时尝试开关空调。通过一端时间的使用,感觉还不错。不用担心忘记关空调了。

附上完整的代码: