2026年3月

串行接力式多智能体协作系统:完整设计方案

这是一套可控、可预测、能产出的多智能体协作系统,模拟人类团队的流水线作业方式。

一、核心理念:从"对话"到"接力"

1.1 并联的局限

当前主流多智能体系统(AutoGen、LangChain Agents、Claude Code)采用并联模式

用户消息 ──┬──> Agent A ──> 响应(竞争)
          ├──> Agent B ──> 响应(竞争)
          ├──> Agent C ──> 响应(竞争)
          └──> Agent D ──> 响应(竞争)

问题: - 多Agent同时响应,谁先回复用谁 → 不可控 - 适合快速头脑风暴 → 不适合工程产出 - 质量依赖"运气" → 无法保证交付标准

1.2 串行接力的本质

串行接力模拟的是人类团队的流水线作业

需求 ──> 架构设计 ──> 验收标准 ──> 开发实现 ──> 交付
         ↓              ↓            ↓
       [质量门]      [质量门]     [质量门]

核心思想: - 每个角色是流水线上的一个工位 - 每个工位有明确的输入标准和输出标准 - 质量不依赖运气,而依赖流程把关

这才是工程团队的正确姿势。


二、系统架构:文件接力机制

2.1 通信协议:单一信箱制

原则:
  1. 每个Agent有且只有一个"信箱文件"
  2. 谁读取文件,谁负责处理
  3. 处理完毕后必须:
     - 归档日志(保留记录)
     - 清空信箱(表示"已完成")
     - 写入下一个信箱(传递接力棒)

2.2 目录结构

/project_root/
├── channels/                    # 通信信道(核心!)
   ├── to_architect.md          # 人类 -> 架构师
   ├── to_qa.md                # 架构师 -> 质量标准制定
   ├── to_developer.md         # 质量标准 -> 开发
   └── to_human.md             # 架构师/开发 -> 人类

├── tasks/                      # 任务清单
   ├── task_001.md
   └── ...

├── history_logs/               # 历史归档
   └── ...

└── workspace/                  # 代码工作区

2.3 轮询思想

每个Agent是一个独立进程,循环检测自己的信箱: - 信箱空 → 等待 - 信箱有内容 → 读取、处理、归档、清空、传递


三、任务依赖与智能跳过机制

3.1 为什么需要任务依赖

问题:传统串行流程中,人类成为绝对瓶颈

需求 → 架构师 → [人类决策] → QA → 开发 → 交付
                     ↑
                 卡住不动

现实:真实项目中,任务之间往往不是强依赖的

举例:开发"用户系统"
├── 任务A: 用户注册(依赖: 无)      → 可以先做
├── 任务B: 用户登录(依赖: A)        → 可以先做
├── 任务C: 微信登录(依赖: B, 需要人类决策)→ 等待
└── 任务D: 忘记密码(依赖: 无)       → 可以先做

3.2 任务依赖标记

--- TASK START ---
ID: 003
DESC: 实现微信扫码登录
依赖: 002
需要人类决策: 是
决策项:
  - 微信扫码回调地址
  - 扫码超时时间
--- TASK END ---

3.3 智能跳过逻辑

当任务T需要人类决策时:
1. 检查下一个任务T+1
2. 如果T+1的"依赖"不包含T → 跳过T+1,继续检查T+2
3. 如果T+1依赖T → 等待人类决策
4. 重复直到找到可执行的任务

四、角色定义与协作流程

4.1 三大角色(精简版)

角色 职责 输入 输出
架构师 需求分析、任务拆解、技术决策、安全要求 人类需求 任务清单+安全要求
质量标准制定者 验收标准制定、安全测试 任务清单 验收标准+安全测试
开发者 代码实现、技术攻关 任务+验收标准 可运行代码

设计亮点:移除Reviewer,安全左移到Designer和QA,流程更精简。

4.2 为什么移除Reviewer?

原问题: - 可维护性在快速迭代阶段不重要,功能优先 - 安全问题应该左移,而非最后审查 - 流程过长(4个角色),效率低

解决思路: - 可维护性:事后一次性重构,不在开发过程中纠结 - 安全左移:Designer和QA在源头控制 - 流程精简:Designer → QA → Developer → 完成

4.3 角色关系图

                    ┌─────────────────────┐
                    │      人类 (Human)    │
                    │  - 下发需求         │
                    │  - 接收汇报         │
                    │  - 批量决策        │
                    └─────────┬───────────┘
                              │
              to_architect    │    to_human
                              ▼
                    ┌─────────────────────┐
                    │    架构师 (Architect) │
                    │  - 需求分析          │
                    │  - 任务拆解          │
                    │  - 安全要求          │
                    └─────────┬───────────┘
                              │
              to_qa           │    to_qa
                              ▼
                    ┌─────────────────────┐
                    │  质量标准 (QA)         │
                    │  - 验收标准制定      │
                    │  - 安全测试策略      │
                    └─────────┬───────────┘
                              │
              to_developer     │    to_human
                              ▼
                    ┌─────────────────────┐
                    │  开发 (Developer)     │
                    │  - 代码实现          │
                    │  - 功能优先          │
                    └─────────────────────┘

4.4 详细协作流程

阶段一:需求澄清(Human ↔ Architect)

步骤1: 人类下发需求
  to_architect.md:
  需求:实现用户登录系统
  - 用户名密码登录
  - 微信扫码登录

步骤2: 架构师分析
  - 识别模糊点  向人类提问
  - **明确安全要求**

步骤3: 任务拆解(含安全要求)
  to_qa.md:
  TASK_001: 用户名密码注册
  技术要求:
    - 用户名密码注册
    - bcrypt加密存储
    - JWT Token返回
  安全要求:
    - 使用参数化查询防止SQL注入
    - 密码强度验证(至少8位)

阶段二:验收标准对齐(Architect → QA)

步骤4: 质量标准制定者接收任务
  - 阅读架构师的任务拆解
  - **制定验收标准**
  - **制定安全测试策略**

步骤5: 制定验收标准
  to_developer.md:
  TASK_001: 用户名密码注册
  验收标准:
    1. 用户名4-20位字母数字
    2. 密码至少8位,含数字和字母
    3. 用户名已存在返回400
    4. 注册成功返回200+token
  安全测试:
    - SQL注入测试:尝试 ' OR '1'='1
    - XSS测试:尝试 <script>alert(1)</script>

阶段三:开发执行(Developer)

步骤6: 开发者接收任务+验收标准
  - 理解任务要求
  - 理解"什么是完成"
  - **功能优先,快速实现**

步骤7: 智能跳过
  - 当前任务需要等待人类决策?
  - 检查后续任务是否依赖当前任务
  - 不依赖  跳过执行

步骤8: 开发完成
  to_human.md:
  TASK_001: 用户名密码注册
  状态:  已完成
  改动:
    - models/user.py
    - routes/register.py

五、SKILL.md 设计要点

5.1 架构师 SKILL.md

# Role: 架构师

## 职责
- 需求分析、任务拆解
- 技术选型
- **安全要求明确**

## 安全要求模板
 安全要求:
   - 使用参数化查询防止SQL注入
   - 密码使用bcrypt/argon2加密
   - 敏感配置通过环境变量管理
   - 验证码/限流防止暴力攻击

## 输出格式
--- TASK START ---
ID: 001
DESC: 实现用户注册接口
技术要求:
  - 用户名密码注册
  - bcrypt加密存储
安全要求:
  - 参数化查询
  - 密码强度验证
依赖: 无
需要人类决策: 否
--- TASK END ---

5.2 质量标准制定者 SKILL.md

# Role: 质量标准制定者 (QA)

## 职责
- 验收标准制定
- **安全测试策略**

## 安全测试模板
 安全测试:
   - SQL注入: ' OR '1'='1
   - XSS: <script>alert(1)</script>
   - 密码强度: 不足8位应拒绝
   - 用户名重复: 返回400错误

## 输出格式
--- TASK START ---
ID: 001
DESC: 实现用户注册接口

验收标准:
  1. 用户名4-20位字母数字
  2. 密码至少8位

安全测试:
  - SQL注入测试
  - XSS测试
  - 密码强度测试
--- TASK END ---

5.3 开发者 SKILL.md

# Role: 开发工程师

## 核心理念
- **功能优先,快速实现**
- 可维护性不是首要考虑
- 重构可以事后一次性做

## 工作流程
1. 理解任务和验收标准
2. 快速实现功能
3. 确保通过基本的安全测试(参数化查询等)
4. 完成后输出到 to_human.md

## 输出格式
--- COMPLETE ---
TASK_ID: 001
状态: ✅ 已完成
改动文件:
  - models/user.py
  - routes/register.py
---

六、与现有系统的整合

6.1 模式选择

场景 推荐模式 原因
快速问答 并联 多角度即时响应
头脑风暴 并联 发散思维
功能开发 串行 质量可控+高效

6.2 启动串行模式

用户输入: "/workflow 开发一个用户登录系统"
→ 进入串行流程(支持任务依赖+智能跳过)

七、独特优势

7.1 绝对可观测性

$ ls -la channels/

-rw-r--r-- 1 root root  120 Mar 22 09:35 to_developer.md  # 接力棒在开发
-rw-r--r-- 1 root root    0 Mar 22 09:30 to_architect.md    # 空
-rw-r--r-- 1 root root    0 Mar 22 09:25 to_qa.md            # 空

7.2 天然串行化 + 智能跳过

T001(完成) → T002(等待人类) → T003(不依赖T002) → 执行T003
                        ↓
              人类决策后恢复执行T002

7.3 完整的审计日志

history_logs/
├── 2026-03-22-093000_to_architect.md   # 人类需求
├── 2026-03-22-093010_to_qa.md          # 架构师任务
├── 2026-03-22-093020_to_developer.md  # QA验收标准
├── 2026-03-22-093100_to_architect.md  # 开发者提问
├── 2026-03-22-093200_to_human.md     # 完成报告

7.4 极简实现

不需要: - ❌ 消息队列 - ❌ WebSocket长连接 - ❌ 分布式锁 - ❌ 评审角色

只需要: - ✅ 文件系统 - ✅ 定时轮询 - ✅ LLM API调用


八、设计哲学:可控 >自治

8.1 安全左移

传统流程:开发 → 评审(审查安全)→ 发现问题 → 返工 我们的流程:Designer(安全要求)→ QA(安全测试)→ 开发(直接通过)

好处: - 安全问题在源头控制 - 减少返工 - 开发只需遵循既定要求

8.2 功能优先

核心理念: - 快速实现功能是第一优先级 - 可维护性可以事后重构 - 不在开发过程中纠结架构

重构时机: - 功能稳定后 - 一次性重构 - 借助专门的重构Agent

8.3 业界参考

借鉴业界五大编排模式(Microsoft Azure定义):

模式 描述 适用场景
Sequential 线性串行 多阶段流程
Handoff 控制权交接 任务路由
Magnetic 动态邀请 开放任务

我们的选择:Sequential + Handoff + 智能跳过。

8.4 核心原则

工程产出 = 流程 × 规范 × 质量门 × 智能调度

我们追求的: - 功能快速产出 - 安全源头控制 - 质量靠流程保证


九、局限性与演进

9.1 当前局限

  1. 轮询延迟:5秒轮询间隔
  2. 单lane执行:同时只执行一个任务
  3. 无断点续传:进程重启后状态丢失

9.2 演进路线

v1.0 (当前)
  ✅ 三大角色(架构师+QA+开发)
  ✅ 任务依赖+智能跳过
  ✅ 安全左移
  ✅ 功能优先

v2.0
  🔄 事件驱动(替代轮询)
  🔄 多lane并行

v3.0
  📋 可视化监控
  📋 智能调度优化

十、总结

串行接力式多智能体协作系统,是对人类工程团队协作方式的数字化模拟

  • 架构师 = 项目经理 + 技术负责人 + 安全要求制定
  • 质量标准制定者 = QA分析师 + 测试策略 + 安全测试
  • 开发者 = 功能实现者(快速优先,可维护性可后处理)

核心价值

  1. 可控:流程由工作流定义
  2. 可观测:文件状态即系统状态
  3. 可追溯:每个决策都有完整记录
  4. 更高效:三大角色,流程精简
  5. 安全左移:Designer和QA源头控制
  6. 功能优先:快速产出,可维护性可后处理

在AI时代,我们需要的是可控的协作流程,而非完全自治的黑盒系统。


本文是《串行接力式多智能体协作规范》的深度扩展,形成了一套完整的设计方案。

如何用Python控制米家智能设备 - 完整指南

背景

米家(小米智能家居)是国内最大的IoT平台之一,支持数千种智能设备。很多用户希望能通过编程方式控制这些设备,而不仅限于使用米家App。

本文是作者实际打通米家设备控制的完整记录,包括技术方案、踩坑总结、安全分析等。无论你是想学习IoT控制,还是想自己搭建智能家居系统,本文都能提供有价值的参考。


一、米家设备控制的技术原理

1.1 MiIO vs MIoT 协议

通过搜索发现,米家设备主要使用两种协议:

协议名称设备类型特点
miIO传统协议旧设备需要手动探索控制方式
MIoT现代协议新设备自动获取设备规格(更先进)

关键发现:python-miio官方文档明确指出:

"Most modern (MIoT) devices are automatically supported by the generic miot integration. Internally, it uses miot spec files to find out about supported features..."

翻译:大多数现代(MIoT)设备被generic miot集成自动支持。内部使用miot spec文件来获取支持的功能。

1.2 miot-spec.com 设备规格网站

通过搜索发现有一个关键网站:home.miot-spec.com(米家设备规格数据库)

这是米家设备的"身份证",记录了各种设备的siid/aiid/piid对应关系。

1.3 设备模型:服务-属性-操作

MiIO/MIoT协议的核心是服务-属性-操作模型:

设备 (Device)
├── 服务1 (siid=1: 设备信息)
│   ├── 属性 (piid=1: 设备名称)
│   └── 属性 (piid=2: 固件版本)
├── 服务2 (siid=2: 电源)
│   ├── 属性 (piid=1: 开关状态: on/off)
│   ├── 操作 (aiid=1: 打开)
│   └── 操作 (aiid=2: 关闭)
├── 服务3 (siid=3: 灯光)
│   ├── 属性 (piid=1: 亮度: 0-100)
│   └── 操作 (aiid=1: 设置亮度)
└── ...
  • siid (Service ID):服务ID,设备的功能模块(电源、灯光、马达等)
  • piid (Property ID):属性ID,服务的属性(状态、数值等)
  • aiid (Action ID):操作ID,服务的可执行动作(开、关、开始、停止等)

1.4 厂商实现差异(核心问题)

这是打通设备控制的核心问题:不同厂商对协议的实现差异巨大。

厂商设备类型siid=2的定义aiid=1paramsaiid=3
石头科技扫地机器人电源服务开始清扫[]暂停
Yeelight智能灯电源服务打开["on"]关闭
云米空气净化器电源服务打开["on"]关闭
追觅吸尘器电源服务打开[1]关闭

可以看到:即使是同一个"开"功能:

  • 石头科技:用 aiid=1,params=[](空数组)
  • Yeelight:用 aiid=1,params=["on"](字符串)
  • 追觅:用 aiid=1,params=[1](数字)

这正是用API控制的难点所在。


二、调研过程

2.1 搜索方法

本次调研使用了以下搜索渠道:

  1. GitHub搜索:搜索Python库的stars数和功能介绍
  2. 官方文档分析:分析python-miio官方文档(python-miio.readthedocs.io)
  3. 设备规格网站:发现miot-spec.com设备规格数据库
  4. 实际测试:在真实设备上测试控制命令

2.2 搜索结果

库/资源来源关键信息
python-miioGitHub (4189 stars)支持MIoT自动获取设备规格
mijia-apiGitHub (506 stars)封装了登录和设备操作
miot-spec.com网站设备规格数据库

2.3 python-miio的设备发现机制

通过搜索python-miio官方文档,发现其设备发现机制:

  1. mDNS发现:通过mDNS自动发现局域网设备,获取设备类型信息
  2. Handshake发现:通过握手获取设备token,但无法获取设备类型
  3. 云端获取:通过micloud从云端获取所有设备的token

官方文档说明:

"The miiocli tool can fetch the tokens from the cloud if you have micloud package installed.
Executing the command will prompt for the username and password, as well as the server locale to use for fetching the tokens."

2.4 两种方案的详细对比

特性mijia-apipython-miio
协议支持MiIOMiIO + MIoT
设备发现云端获取局域网发现 + 云端获取
控制参数需手动探索自动获取
学习曲线简单较陡
维护状态个人维护社区活跃
适用场景快速上手深度定制

建议

  • 新手入门:选mijia-api
  • 需要支持大量设备:选python-miio

三、技术实现

3.1 环境准备

# 安装依赖
pip install mijiaAPI qrcode pillow

3.2 扫码登录(mijia-api方式)

重要:login()会阻塞120秒,需要保持进程活着!

from mijiaAPI import mijiaAPI

api = mijiaAPI()

# login()会阻塞120秒,等待扫码
# 需要保持进程活着,扫码后服务器轮询确认
result = api.login()

if result:
    print("登录成功!")
    # result包含: serviceToken, passToken, userId, expireTime
    print(f"用户ID: {result.get('userId')}")
    print(f"过期时间: {result.get('expireTime')}")
else:
    print("登录失败")

3.3 获取设备列表

devices = api.get_devices_list()

for d in devices:
    print(f"设备名称: {d.get('name')}")
    print(f"设备ID: {d.get('did')}")  # 控制用
    print(f"设备型号: {d.get('model')}")  # 查询规格用
    print(f"在线状态: {d.get('online')}")
    print("---")

3.4 控制设备

# 格式说明
action = {
    "did": "设备ID",      # 设备的唯一标识(从设备列表获取)
    "siid": 服务ID,       # 功能模块(2=电源,3=灯光等)
    "aiid": 操作ID,        # 具体操作(1=开,2=关等)
    "params": [参数]      # 操作参数
}

result = api.run_action(action)

# 返回格式
# 成功: {"code": 0, "message": "成功", "exe_time": 10}
# 失败: {"code": -704040005, "message": "Action不存在"}

3.5 获取设备属性

# 获取设备属性
props = api.get_devices_prop([
    {"did": "设备ID", "siid": 2, "piid": 1}
])
# 返回: {"did": "xxx", "siid": 2, "piid": 1, "value": "on", "code": 0}

3.6 设置设备属性

# 设置设备属性
props = api.set_devices_prop([
    {"did": "设备ID", "props": {"power": "on"}}
])

3.7 完整示例:获取所有设备并分类

from mijiaAPI import mijiaAPI

api = mijiaAPI()
api.login()

devices = api.get_devices_list()

# 按类型分类
lights = []
vacuums = []
others = []

for d in devices:
    model = d.get('model', '')
    if 'light' in model or '灯' in d.get('name', ''):
        lights.append(d)
    elif 'vacuum' in model or '扫地' in d.get('name', ''):
        vacuums.append(d)
    else:
        others.append(d)

print(f"灯: {len(lights)}个")
for l in lights:
    print(f"  - {l.get('name')}: {l.get('did')}")

print(f"扫地机器人: {len(vacuums)}个")
for v in vacuums:
    print(f"  - {v.get('name')}: {v.get('did')}")

print(f"其他设备: {len(others)}个")

四、封装成REST API服务

4.1 完整示例

from flask import Flask, request, jsonify
from mijiaAPI import mijiaAPI
import threading
import qrcode

app = Flask(__name__)
mi_api = None

def generate_qr(url):
    """生成二维码"""
    qr = qrcode.QRCode(version=1, box_size=10, border=5)
    qr.add_data(url)
    qr.make(fit=True)
    img = qr.make_image(fill_color="black", back_color="white")
    img.save("static/mijia-login-qr.png")

def do_login():
    """后台登录"""
    global mi_api
    mi_api = mijiaAPI()
    try:
        result = mi_api.login()
        print(f"登录结果: {result is not None}")
    except Exception as e:
        print(f"登录错误: {e}")

@app.route("/login", methods=["POST"])
def login():
    """触发扫码登录"""
    global mi_api
    
    if mi_api is not None:
        return jsonify({"message": "已登录"})
    
    # 启动后台登录线程
    login_thread = threading.Thread(target=do_login)
    login_thread.start()
    
    return jsonify({
        "message": "请在120秒内用米家APP扫码",
        "qr_url": "/static/mijia-login-qr.png"
    })

@app.route("/devices")
def devices():
    """获取设备列表"""
    if not mi_api:
        return jsonify({"error": "请先登录"}), 401
    return jsonify(mi_api.get_devices_list())

@app.route("/control", methods=["POST"])
def control():
    """控制设备"""
    if not mi_api:
        return jsonify({"error": "请先登录"}), 401
    
    data = request.json
    result = mi_api.run_action({
        "did": data["did"],
        "siid": data["siid"],
        "aiid": data["aiid"],
        "params": data.get("params", [])
    })
    return jsonify(result)

@app.route("/status")
def status():
    """检查登录状态"""
    return jsonify({"logged_in": mi_api is not None})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

4.2 客户端调用示例

# 登录
curl -X POST http://localhost:8080/login

# 获取设备
curl http://localhost:8080/devices

# 开灯
curl -X POST http://localhost:8080/control 
  -H "Content-Type: application/json" 
  -d '{"did": "123456", "siid": 2, "aiid": 1, "params": ["on"]}'

# 关灯
curl -X POST http://localhost:8080/control 
  -H "Content-Type: application/json" 
  -d '{"did": "123456", "siid": 2, "aiid": 1, "params": ["off"]}'

五、踩坑记录与解决方案

5.1 常见错误代码

错误代码含义原因解决
-704040005Action不存在siid或aiid错误尝试其他组合
-705201023Property不可写属性只读使用action而非property
-8data type not validparams参数类型错误检查params格式:字符串/数字/数组
-4008设备不在线设备离线检查设备网络
-401未登录token失效重新扫码登录

5.2 探索设备命令的方法

对于mijia-api这种库,需要手动探索siid/aiid组合:

def explore_device(api, device_id):
    """探索设备的控制命令"""
    results = []
    
    print(f"开始探索设备: {device_id}")
    
    for siid in range(1, 20):
        for aiid in range(1, 20):
            # 尝试不同的参数
            params_list = [
                [],              # 空数组
                ["on"],          # 字符串开
                ["off"],         # 字符串关
                [1],             # 数字开
                [0],             # 数字关
                ["toggle"],      # 切换
            ]
            
            for params in params_list:
                try:
                    result = api.run_action({
                        "did": device_id,
                        "siid": siid,
                        "aiid": aiid,
                        "params": params
                    })
                    
                    if result.get("code") == 0:
                        finding = {
                            "siid": siid,
                            "aiid": aiid,
                            "params": params,
                        }
                        results.append(finding)
                        print(f"✅ 成功! {finding}")
                        
                except Exception as e:
                    pass
    
    print(f"探索完成,找到 {len(results)} 个有效命令")
    return results

5.3 已验证的命令

设备siidaiidparams功能
落地灯21["on"]
落地灯21["off"]
扫地机器人21[]开始清扫
扫地机器人22[]回充
扫地机器人23[]停止/暂停

六、问题分析:为什么用API控制这么复杂?

6.1 米家APP帮用户做了什么

用户用米家App控制设备时,不需要关心siid/aiid/params,因为APP已经封装好了。APP内部维护了每个设备的控制参数映射表,用户只需要点击"开/关"按钮。

6.2 用API控制的问题

用Python API控制时:

  • 没有封装:需要自己处理协议细节
  • 文档缺失:厂商不会公开siid/aiid的对应关系
  • 每个设备不同:即使是同一功能,控制参数也不同
  • 没有统一标准:各厂商按自己的理解实现

这解释了为什么用Python API控制比用App复杂很多倍——因为App帮用户屏蔽了所有这些复杂性。

6.3 更好的方案选择

通过搜索发现,python-miio 提供了更先进的方案:

  1. 自动获取设备规格:MIoT设备自动从miot-spec.com下载规格
  2. 命令行工具:miiocli可以直接控制设备
  3. 无需手动探索:库自动处理siid/aiid
  4. 设备发现:自动发现局域网内的设备

七、安全分析

7.1 token机制

扫码登录后获取的token包含:

  • serviceToken:API调用凭证,有效期数月到数年
  • passToken:用户验证凭证
  • userId:用户标识

7.2 风险评估

理论风险

  • 拿到token可以调用API控制设备
  • 可以获取设备列表
  • 可以执行已创建的场景

实际情况

  • token有有效期(较长)
  • 可能与服务IP绑定(社区讨论多,缺乏权威资料)
  • 官方可以强制下线

7.3 防护建议

  1. 不暴露token:不要提交到GitHub或发到群里
  2. 定期检查:米家App → 设置 → 账号与安全 → 登录设备
  3. 异常处理:发现异常立即修改密码并重新登录
  4. 最小权限:测试用小号,不要用主要账号

7.4 紧急处理

如果怀疑token泄露:

  1. 修改小米账号密码
  2. 米家App退出所有设备登录
  3. 重新扫码获取新token

八、实际应用场景

8.1 定时任务

import schedule
import time
from mijiaAPI import mijiaAPI

def morning_routine():
    """早间routine"""
    api = mijiaAPI()
    api.login()
    
    # 开灯
    api.run_action({"did": "灯ID", "siid": 2, "aiid": 1, "params": ["on"]})

def night_routine():
    """晚间routine"""
    api = mijiaAPI()
    api.login()
    
    # 关灯
    api.run_action({"did": "灯ID", "siid": 2, "aiid": 1, "params": ["off"]})
    # 扫地机器人回充
    api.run_action({"did": "机器人ID", "siid": 2, "aiid": 2, "params": []})

# 设置定时
schedule.every().day.at("07:00").do(morning_routine)
schedule.every().day.at("22:00").do(night_routine)

while True:
    schedule.run_pending()
    time.sleep(60)

8.2 命令行工具

#!/usr/bin/env python3
import sys
from mijiaAPI import mijiaAPI

COMMANDS = {
    "on": {"siid": 2, "aiid": 1, "params": ["on"]},
    "off": {"siid": 2, "aiid": 1, "params": ["off"]},
    "start": {"siid": 2, "aiid": 1, "params": []},
    "stop": {"siid": 2, "aiid": 3, "params": []},
    "return": {"siid": 2, "aiid": 2, "params": []},
}
def main():
    if len(sys.argv) < 3:
        print("用法: miot <设备ID> <命令>")
        print(f"可用命令: {list(COMMANDS.keys())}")
        sys.exit(1)
    
    did = sys.argv[1]
    cmd = sys.argv[2].lower()
    
    if cmd not in COMMANDS:
        print(f"未知命令: {cmd}")
        sys.exit(1)
    
    api = mijiaAPI()
    action = COMMANDS[cmd].copy()
    action["did"] = did
    
    result = api.run_action(action)
    
    if result.get("code") == 0:
        print("成功!")
    else:
        print(f"失败: {result.get('message')}")

if __name__ == "__main__":
    main()

使用:

python miot.py 123456789 on   # 开灯
python miot.py 123456789 off  # 关灯
python miot.py 123456789 start # 开始清扫

九、总结

9.1 核心要点

  1. 协议层面:MiIO(传统)和MIoT(现代)两种协议,后者支持自动获取设备规格
  2. 方案层面

    • mijia-api:需要手动探索siid/aiid,上手简单
    • python-miio:自动获取,更先进但学习曲线较陡
  3. 搜索发现:miot-spec.com是关键的设备规格数据库
  4. 核心问题:厂商实现差异大,没有统一标准,导致控制参数需要逐一探索
  5. 安全层面:token机制存在风险,建议做好防护
  6. 应用层面:可封装REST API、命令行工具,定时任务等多种形式

9.2 下一步建议

  1. 尝试python-miio方案,体验自动获取设备规格
  2. 探索更多设备的控制命令,丰富命令库
  3. 搭建完整的智能家居控制系统

参考资料

  • GitHub - python-miio (4189 stars)
  • GitHub - mijia-api (506 stars)
  • python-miio官方文档 (python-miio.readthedocs.io)
  • miot-spec.com 设备规格网站