Table Of Contents

  • Categories
  • iot

目标

使用开源软件和米家智能硬件,实现简单的智能家居系统

采用米家智能硬件的原因是, 小米智能家居做的比较早(也许是起初推广的更好,刚接触智能家居的时候就用的小米), 另外一点是小米智能硬件确实物美价廉,在国外也很受欢迎,Github上也有很多相关项目.

环境和设备

  • RaspberryPi 4 (4GB版本)
  • Ubuntu arm64 树莓派版本

安装HomeAssistant

这里使用Ubuntu而不是Raspbian的一个原因是Ubuntu官方软件包更新更快(比如raspbian当前为3.7而hass需要3.8+版本), 且自己笔记本一直用Ubuntu,统一发行版使用起来更加方便

sudo pip3 install homeassistant

如果下载很慢,可以考虑使用pypi镜像源

编辑/etc/pip.conf (使用豆瓣源)

[global]
index-url = https://pypi.douban.com/simple

设置开机自启动

  • 创建systemd service unit文件
systemctl --user edit --full --force homeassistant.service

加入以下内容

[Unit]
Description=Home Assistant
After=network.target

[Service]
Type=simple
ExecStart=/home/ubuntu/.local/bin/hass
Restart=on-failure
RestartSec=5

[Install]
WantedBy=default.target
  • 设置用户自动登录

安装raspi-config, 然后配置 1. System Options -> S5 Boot / Auto Login -> B2 Console Autologin

会生成/etc/systemd/system/getty@tty1.service.d/autologin.conf, 内容如下

[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin ubuntu --noclear %I $TERM

重启即可生效

常用设备接入

这里主要使用小米硬件设备,开始之前需要获取各个设备的token。参考homeassistant获取miio token的教程

目前推荐使用云端获取的方式 https://github.com/PiotrMachowski/Xiaomi-cloud-tokens-extractor

设备协议mihome-binary-protocol

  • homeassistant 参考配置

.homeassistant/configuration.yaml

# Configure a default setup of Home Assistant (frontend, api, etc)
default_config:

homeassistant:
  allowlist_external_dirs:
    - "/mnt/usb/media"
  media_dirs:
    local: /mnt/usb/media

# Text to speech
tts:
  - platform: google_translate

rpi_camera:
  image_quality: 100
  timelapse: 5000
  file_path: /mnt/usb/media/camera/snapshot.jpg
  

group: !include groups.yaml
automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
light: !include lights.yaml
yeelight: !include yeelight.yaml
remote: !include remote.yaml
stream:
media_player: !include media_player.yaml
device_tracker: !include device_tracker.yaml
shell_command: !include shell.yaml
telegram_bot: !include telegram.yaml
notify: !include notify.yaml

插座/灯泡

小米插座chuangmi.plug.m1和灯泡(这里我用的是飞利浦灯泡), 使用[python miio]即可以控制。

可以在homeassistant管理界面添加Xiaomi Miio集成。

网关

小米网关提供了ZigBee和蓝牙设备接入和管理的功能。

型号: lumi.gateway.mgl03

升级官方新版本固件后,不能直接接入。可以刷入第三方固件,然后接入homeassistant。

  • 刷机

telnet登录网关,按照GitHub上的此教程执行刷机步骤

如果访问不了Github下载链接,可以使用代理,例如设置环境变量 https_proxy=socks5://192.168.50.60:1080

红外遥控器

红外遥控器也可以使用xiaomi_miio接入homeassistant。

型号: chuangmi.remote.v2

配置示例remote.yaml(示例里的raw command我瞎填的, 根据自己的实际需要写)

- platform: xiaomi_miio
  name: 'chuangmi.remote.v2'
  host: 192.168.50.72
  token: <token>
  slot: 1
  timeout: 30
  hidden: false
  commands:
    # haier
    air_conditioner_turn_on:
       command:
         - raw:mcwm84lgAkzSazOcSyazScSyYzacTYAhAA3AQ8BjwIfAZ8DfwIfAh8APwA:38400
    air_conditioner_turn_off:
       command:
         - raw:mcwm84lgAkzSazOcSyazScSyYzacTYAhAA3AQ8BjwIfAQ8BDwO/Bd8APwA/AD8APwA/AD8APwA/AD8AP:38400
    air_conditioner_cool:
       command:
         - raw:mcwm84lgAkzSazOcSyazScSyYzacTYAhAA3AQ8BjwIfAQ8BDwO/Bd:38400
    magic_ball_turn_on:
      command:
        - raw:nMxm8wlk0mk2mEsm0ymEsmsxAHKYADlMwB2mgB/ADmBP4B9TGbAYMAR4BFApoBmUxAEMAjwNPA0gJCwcHCI8A/wJ6BjoKrwCOCHuYQA=
    magic_ball_turn_off:
      command:
        - raw:nMxmswlk0mk4mEsm0ymEsmsxAHKYADlNAD+mYA9AL+APUxmwHDTGbAQsAjQAhAdwAhYBCgV2BFoR7g/+Ae01nID/g2cBAYLShH+AjQDXgEJMIAA=

然后我们可以在scripts.yaml里定义常用的操作(这些操作可以在安卓的设备控制器快捷方式找到)

'air_conditioner_turn_on':
  alias: '打开空调(睡眠模式)'
  sequence:
    - service: remote.send_command
      entity_id: 'remote.chuangmi_remote_v2'
      data:
        command:
          - 'air_conditioner_turn_on'
'air_conditioner_turn_off':
  alias: '关闭空调'
  sequence:
    - service: remote.send_command
      entity_id: 'remote.chuangmi_remote_v2'
      data:
        command:
          - 'air_conditioner_turn_off'

'air_conditioner_cool':
  alias: '空调制冷'
  sequence:
    - service: remote.send_command
      entity_id: 'remote.chuangmi_remote_v2'
      data:
        command:
          - 'air_conditioner_cool'

'magic_ball_turn_on':
  alias: '打开小夜灯'
  sequence:
    - service: remote.send_command
      entity_id: 'remote.chuangmi_remote_v2'
      data:
        command:
          - 'magic_ball_turn_on'
'magic_ball_turn_off':
  alias: '关闭小夜晚灯'
  sequence:
    - service: remote.send_command
      entity_id: 'remote.chuangmi_remote_v2'
      data:
        command:
          - 'magic_ball_turn_off'

效果如下

学习红外码

接入遥控器后,我们可以通过它学习红外码。进入开发者工具 -> 服务,选择xiaomi_miio.remote_learn_command

点击调用服务,然后把遥控器对准米家万能遥控器按下要学习的按键,就可以学习了。识别的红外码会出现在homeassistant 的通知那里, 可以配置到上面yaml的raw command。(在使用前,可以用remote.send_command测以下)

刚好别人送我一个小夜灯,试了一下,可以智能地控制了。

Hack红外码

使用xiaomi_miio.remote_learn_command方式只能学习简单的红外码。对于复杂的红外码,比如空调,遥控器学习识别的红外码不起作用 (我傻乎乎地尝试了好久才发现这个知识)

解决方式之一是, 你知道空调的各个按键的raw command是什么,即使用红外码库。查了一下,国外有一些开源的红外码库,但没有国内的设备型号, 而且设备类型比较少。

我使用的是海尔空调,只找到https://github.com/crankyoldgit/IRremoteESP8266库,可能有可以利用的信息(有测试代码), 但是它使用的是pronto格式,支持的设备类型也有限。

后面发现了硬核的hack方式, 可以通过串口连上遥控器的控制台,从调试日志中获取红外码。

这里简单说一下步骤。

  • 焊接

对于一个新手来说,焊接PCB板是一个比较麻烦的事情。基于大学时,曾焊接制作过一个完整收音机的经历,我开始动手了。

如图,拆完后焊点是PCB板上的三个圆形金属接触点(TX, RX, GND)。可能不是设计用来焊接的(我猜测应该是有专门的金属探头压在上面,使电路接通),先要将 导线镀焊,不然焊接会颇为麻烦, 不太容易焊上。

PS, 虽然焊接这个成功了,但是小米台灯焊接后却没能连上, 不过它可以接入到Google Home,作为一个桌面上的台灯也没必要太多的智能控制场景了

  • 串口调试

连接好USB串口线后,启动电源。

在笔记本上安装好screen软件, 执行以下命令

screen -L /dev/ttyUSB0 115200

# Ctrl + A + : 打开screen菜单

日志将输出到screenlog.0, 随着你在米家App上按下空调键,可以从中看到有用的日志信息(token)。

这里给出部分日志(被我删除处理过敏感信息了)

------Now net status:4------
23:06:44.740 [D] otu: [192.168.50.32:51222] talking, len=32
23:06:44.740 [W] otu: Token private!!. (otu_packet_handle,706)
23:06:44.750 [D] otu: [192.168.50.32:51222] talking, len=32
23:06:44.750 [W] otu: Token private!!. (otu_packet_handle,706)
23:06:44.760 [D] otu: [192.168.50.32:51222] talking, len=32
23:06:44.760 [W] otu: Token private!!. (otu_packet_handle,706)
23:06:44.770 [D] otu: [192.168.50.32:57072] talking, len=304
23:06:44.780 [D] otu: local rpc packet.
23:06:44.780 [D] otu: {"id": 43, "method": "miIO.ir_play", "params": {"freq": 38400, "code": "mcwm84lgAkzSazOcSyazScSyYzacTYAhAA3AQ8BjwIfAQ8CrwCPAr8APwA/AD8APwA/AD8APwA/AD8APwA/AD8APwA/AD8APwA/AD8APwA"}}.
23:06:44.810 [D] otu: down rpc_local = 7
23:06:44.810 [D] ot: miIO.ir_play found
------do_ir_play cmd:{"id": 43, "method": "miIO.ir_play", "params": {"freq": 38400, "code": "mcwm84lgAkzSazOcSyazScSyYzacTYAhAA3AQ8BjwIfAQ8CrwCPAr8APwA/AD8APwA/AD8APwA/AD8KDwA/AD8APw6vAh8APwA"}}------
ir_play:Cannot found length
Base64Decode------------>len=143
99,cc,26,f3,89,60,2,4c,d2,6b,33,9c,4b,26,b3,49,c4,b2,63,36,9c,4d,80,21,0,d,c0,43,c0,63,c0,87,c0,43,c0,ab,c0,23,c0,af,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,cf,c0,f,c0f,c0,f,c0,f,c0,f,c0,f,c2,27,c0,53,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c0,f,c5,63,c0,f,c0,a3,c0,a3,c8,93,c0,87,e6,33,9,84,c0,
Base64Decode------------>end
tmp_ct=941
23:06:44.880 [I] user_main: ----origin_code:3078,3078,3078,4,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,1686,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,1686,548,1686,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,1686,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,548,1686,548,548,548,548,548,548,548,548,548,1686,548,548,548,548,548,1686,548,1686,548,548,548,1000

23:06:44.970 [I] splitnums=230

23:06:44.970 [I] item nums=120

last = 1000
wave_data_len=115

-----------------------------

-----send_infrared_fro23:06:m_buf--
len=115
0~2456,1~2456||0~2456,1~3644.990 24||0~432,1~1344||0~432,1~432||0~432,1~1344||0~4[D] otu: {"id":43,"result":["ok"]}
32,1~432||0~432,1~432||0~432,1~1344||0~432,1~1344||0~432,1~432||23:06:45.010 [D] otu
0~432,1~1344||0~432,1~432||0~432,1~1344||0~432,1~1344||0~432,1~1344||0~432,1~1344||0~

日志中的code部分就是红外码了。

和来源的作者一样,我这里也告警一下,红外码库可能涉及到授权和购买, 不要用作商业用途和违法事情

Telegram通知

完成了硬件设备的接入,我们还需要实现自动监控和远程控制。

除了将API托管到云端,还可以通过邮件或者IM工具实现。这里Telegram机器人就可以满足这个需求了。

配置

telegram.yaml

- platform: polling
  proxy_url: http://127.0.0.1:8118
  api_key: <token数字:字符>
  allowed_chat_ids:
    - 123456

- platform: broadcast
  proxy_url: http://127.0.0.1:8118
  api_key: <token数字:字符>
  allowed_chat_ids:
    - 123456

注意这里使用的是http代理,socks5代理貌似有bug

接入语音助手

天猫精灵

Google Assistant

自定义

UI Icons

https://materialdesignicons.com/icon/air-conditioner

usage: mdi:air-conditioner

参考