目标
使用开源软件和米家智能硬件,实现简单的智能家居系统
采用米家智能硬件的原因是, 小米智能家居做的比较早(也许是起初推广的更好,刚接触智能家居的时候就用的小米), 另外一点是小米智能硬件确实物美价廉,在国外也很受欢迎,Github上也有很多相关项目.
系统架构
整个系统以树莓派上的 Home Assistant 为核心:
- 接入层:通过
xiaomi_miio直连米家 WiFi 设备(插座、灯泡、红外遥控器),红外遥控器再控制空调、小夜灯等传统电器;通过刷了第三方固件的小米网关接入 ZigBee / 蓝牙子设备。 - 感知层:树莓派相机用于定时抓拍。
- 交互层:手机 / Web 前端本地访问,通过 Telegram Bot 实现远程通知与控制,接入天猫精灵 / Google Assistant 实现语音控制。
- 云端:设备 Token 通过米家云获取。

HomeAssistant 实现架构
上一节的系统架构展示的是"部署视图",这一节从"实现视角"看 Home Assistant 这个软件本身是怎么工作的。它的核心是事件驱动:一切状态变化都通过事件总线广播,各组件通过订阅事件解耦协作,而不是相互直接调用。
核心三件套
Home Assistant 启动时创建唯一的 hass 对象,作为所有组件的"中枢"。它内部承载三个最核心的子系统:
- 事件总线 (Event Bus,
hass.bus):发布/订阅模型。任何状态变化都会触发state_changed事件,组件也可以定义自定义事件。组件通过hass.bus.async_listen()订阅,通过hass.bus.fire()发布。所有组件之间的通信都走这里,实现完全解耦。 - 状态机 (State Machine,
hass.states):保存所有实体的当前状态。实体通过hass.states.async_set()写入状态(例如light.living_room为on),通过hass.states.get()读取。状态一旦变化,就触发state_changed事件。 - 服务注册表 (Service Registry,
hass.services):可调用的"动作",例如light.turn_on、xiaomi_miio.remote_learn_command。前端、自动化、脚本都通过hass.services.async_call()调用服务,由注册表路由到对应组件处理。
设备如何接入:Integration 体系
功能扩展通过 Integration(集成) 实现,每个集成是 components/<domain>/ 下的一个 Python 包:
- manifest.json:声明依赖、前置条件(如需要特定平台)、需求列表,是集成的"身份证"。
- Config Entry / Config Flow:通过 UI 添加集成时,
config_flow.py引导用户填写配置,生成config entry持久化到.storage/core.config_entries,支持 UI 配置而无需改 YAML。 - Platform:
light.py、sensor.py、switch.py等平台文件定义该设备类型对应的实体类。集成负责把物理设备抽象成一个个 Entity 实例。
实体与注册中心
- Entity:设备在 HA 中的逻辑抽象,继承
LightEntity、SensorEntity等基类。实体通过async_write_ha_state()把状态推送到状态机,从而产生事件。 - 设备/实体注册中心 (.storage/entity_registry):持久化维护每个实体的
unique_id、显示名称、归属设备等,保证实体身份稳定。
数据流:一条完整的状态上报链路
以小米插座为例,一次状态变化的完整链路:
物理设备(插座)
-> 集成适配器(xiaomi_miio) 轮询/推送状态
-> Entity 实例调用 async_write_ha_state()
-> 状态机更新状态
-> 触发 state_changed 事件
-> 事件总线广播
-> 监听者: 自动化触发 / 前端更新 / Recorder 记录历史
指令下发则走反向链路:call_service → 服务注册表 → 实体 → 适配器 → 物理设备。
关键点:事件总线把"采集侧"(设备→状态) 和"执行侧"(自动化/前端→设备) 解耦,新增一个设备类型只需要新增一个 Integration,无需改动核心和已有组件,这就是 Home Assistant 扩展性的来源。
环境和设备
- RaspberryPi 4 (4GB版本)
- Ubuntu arm64 树莓派版本
安装HomeAssistant
这里使用Ubuntu而不是Raspbian的一个原因是Ubuntu官方软件包更新更快(比如raspbian当前为3.7而hass需要3.8+版本), 且自己笔记本一直用Ubuntu,统一发行版使用起来更加方便
在现代 Linux (如 Ubuntu 24.04 及 Python 3.12+) 环境下,推荐使用 Python 虚拟环境 (venv) 进行隔离安装:
# 安装基础构建与 venv 依赖
sudo apt update
sudo apt install -y python3-dev python3-venv build-essential libssl-dev libffi-dev libjpeg-dev zlib1g-dev
# 创建虚拟环境
python3 -m venv ~/homeassistant-venv
# 使用镜像源安装 homeassistant 及前端组件
~/homeassistant-venv/bin/pip install -i https://pypi.tuna.tsinghua.edu.cn/simple homeassistant home-assistant-frontend
也可以通过编辑 ~/.config/pip/pip.conf 配置默认 PyPI 镜像源:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
设置开机自启动
- 创建 systemd service unit 文件
systemctl --user edit --full --force homeassistant.service
加入以下内容
[Unit]
Description=Home Assistant
After=network.target
[Service]
Type=simple
ExecStart=/home/pi/homeassistant-venv/bin/hass
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
- 开启 systemd 用户服务常驻(即使未登录控制台也可开机自启):
sudo loginctl enable-linger pi
systemctl --user daemon-reload
systemctl --user enable --now homeassistant.service
- 设置用户自动登录
安装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 pi --noclear %I $TERM
重启即可生效
常用设备接入

这里主要使用小米硬件设备,开始之前需要获取各个设备的token。参考homeassistant获取miio token的教程
目前推荐使用云端获取的方式 https://github.com/PiotrMachowski/Xiaomi-cloud-tokens-extractor

- 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