宿舍的上网环境是歌华有线,走的方式是很多年前在计算机知识科普书籍上看到的有线电视同轴电缆方式。 不知道是线路问题还是运营商赠送的 modem 问题,经常掉线,而且掉线后需要重启 modem 才能恢复。 这个过程需要手动操作,很麻烦。因为出问题的时候 modem 后台大部分时候也无法访问,所以通过 web 重启的选择也不可行。
所以我想到了用智能插座来控制 modem 的电源。这样就可以通过后台程序来自动重启 modem 了。但是 这里有个悖论问题是,modem 出问题的时候整个网络是不联通的,也就没办法通过互联网控制智能插座。 能不能通过局域网访问和控制智能插座呢?在网上一番搜寻找到了小米智能插座相关的一系列博客文章, 证明了这个方案的可行性。我主要参考了这篇小米智能插座监控设备耗电,并自动断电 感谢作者的分享,也简单记录一下我的实现过程。
我买的具体型号为小米智能插座3,没太研究明白到底有多少类似的款,应该大部分都是能满足要求的。 主要步骤如下:
1. 配置设备🔗
在配置好网络添加进入米家APP后,可以在米家APP或者路由器后台获得智能插座的 IP 地址。
2. 获取设备 Token🔗
采用 Xiaomi-cloud-tokens-extractor 提供的方法获取,运行后可以获取局域网所有设备的 token 和型号等信息。 示例输出:
NAME: Mijia Smart Plug 3
ID: <id>
MAC: <mac>
IP: 192.168.1.1xx
TOKEN: <token>
MODEL: cuco.plug.v3
---------
NAME: Mi Control Hub
ID: <id>
MAC: <mac>
IP: 192.168.1.1xx
TOKEN: <token>
MODEL: lumi.gateway.v3
3. 获取设备接口信息🔗
通过小米/米家产品库网站 https://home.miot-spec.com/ 获取设备的接口信息。
搜索 cuco.plug.v3
找到对应页面 https://home.miot-spec.com/spec/cuco.plug.v3
点击 查看属性和方法
可以看到接口信息,其中 Switch
是控制开关的接口,也是我们主要使用的。
# 获取当前状态
ret = s.raw_command('get_properties', [{'did':'Switch', 'siid':2, 'piid':1}])
print(ret)
# 设置状态:开机/关机
# value: True 关机,False 开机
ret = s.raw_command('set_properties', [{'did':'Switch', 'siid':2, 'piid':1,'value':False}])
print("set_switch_status: ",ret)
4. 系统监控脚本🔗
最终的监控脚本如下,可以配合 systemd 实现开机自启动。
注意:没有采用 ping 的方式监控 modem 的连通性,而是采用 requests 的方式监控 modem 的 web 服务是否正常。
import miio
import time
import requests
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
ip = '192.168.1.1xx'
token = ''
def check_net():
url = 'http://192.168.100.1' # 替换为要检测的网站URL
username = '' # 替换为网站的用户名
password = '' # 替换为网站的密码
session = requests.Session()
try:
response = session.get(url, auth=(username, password),timeout=1)
return(True)
except:
return(False)
def set_switch_status(value):
s = miio.device.Device(ip=ip, token=token)
ret = s.raw_command('set_properties', [{'did':'Switch', 'siid':2, 'piid':1,'value':value}])
logging.info("set_switch_status: " + str(ret))
logging.info('start monitoring network...')
err = 0
while True:
if not check_net():
err +=1
logging.error('err: ' + str(err))
else:
logging.debug('it works!')
err = 0
if err > 3:
logging.info('err: ' + str(err))
logging.critical('switching off...')
set_switch_status(False)
time.sleep(5)
logging.critical('switching on...')
set_switch_status(True)
time.sleep(120)
time.sleep(15)