2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00
2026-07-20 10:56:12 +08:00
2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00
2026-07-19 20:43:49 +08:00
2026-07-20 11:32:06 +08:00
2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00
2026-07-20 11:20:39 +08:00
2026-07-18 22:40:40 +08:00
2026-07-18 22:40:40 +08:00

mijia-go-api

mijia-go-api 是一个纯 Go 米家 API 库,支持二维码登录与 token 刷新、家庭/设备/场景/耗材查询、属性读写、action 执行、统计查询,以及基于 MIoT 设备描述的高级 Device 操作。

本项目只提供 Go 库,不提供 CLI、MCP、skills 或 HAR decrypt 功能。

安装

go get git.misaka.ren/m1saka/mijia-go-api

登录

NewClient 的认证路径传空字符串时,默认使用 ~/.config/mijia-api/auth.jsonLogin 会优先尝试刷新已有 token;需要重新登录时,会在终端输出二维码供米家 App 扫描。

package main

import (
	"context"
	"log"

	mijia "git.misaka.ren/m1saka/mijia-go-api"
)

func main() {
	client, err := mijia.NewClient("")
	if err != nil {
		log.Fatal(err)
	}

	if _, err := client.Login(context.Background()); err != nil {
		log.Fatal(err)
	}

	auth := client.AuthData()
	log.Printf("已登录 userId=%s", auth.UserID)
}

auth.json 包含 serviceTokenpassTokenssecurity 等敏感认证数据。库写入该文件时使用 0600 权限;请保持此权限,并且不要将该文件提交到版本库。

Web/GUI 应用可通过 WithQRWriter 接收登录输出,并在阻塞的 Login 等待期间实时展示给用户:

import (
	"bufio"
	"context"
	"io"

	mijia "git.misaka.ren/m1saka/mijia-go-api"
)

func streamLogin(ctx context.Context, renderLoginLine func(string)) error {
	reader, writer := io.Pipe()
	defer reader.Close()

	client, err := mijia.NewClient("", mijia.WithQRWriter(writer))
	if err != nil {
		writer.Close()
		return err
	}

	loginDone := make(chan error, 1)
	go func() {
		_, err := client.Login(ctx)
		writer.CloseWithError(err)
		loginDone <- err
	}()

	scanner := bufio.NewScanner(reader)
	for scanner.Scan() {
		renderLoginLine(scanner.Text())
	}

	loginErr := <-loginDone
	if loginErr != nil {
		return loginErr
	}
	return scanner.Err()
}

Login 会阻塞等待扫码,因此必须同步消费 Writer 输出。Writer 可能由另一个 goroutine 写入,不能无同步地并发读写 bytes.Buffer。Writer 接收的内容包含登录二维码 URL,属于敏感登录信息;调用方不得将其写入日志、监控事件或其他持久化记录。

底层 API

以下示例展示设备、属性和 action 的直接调用。GetDeviceshomeID 传空字符串时查询所有家庭。

devices, err := client.GetDevices(ctx, "")
if err != nil {
	return err
}

properties, err := client.GetProperties(ctx, []mijia.PropertyRequest{
	{DID: devices[0].DID, SIID: 2, PIID: 1},
})
if err != nil {
	return err
}

_, err = client.SetProperties(ctx, []mijia.PropertySetRequest{
	{DID: devices[0].DID, SIID: 2, PIID: 1, Value: true},
})
if err != nil {
	return err
}

_, err = client.RunActions(ctx, []mijia.ActionRequest{
	{DID: devices[0].DID, SIID: 2, AIID: 1},
})

家庭、场景、耗材和统计分别使用 GetHomesGetScenesRunSceneGetConsumablesGetStatistics

高级 Device

NewDevice 可通过 DeviceSelector.DIDDeviceSelector.Name 选择设备;名称匹配到多个设备时会返回 MultipleDevicesFoundError。设备描述默认缓存到认证文件所在目录,每次成功的 GetSetRunAction 后默认等待 500ms

device, err := mijia.NewDevice(ctx, client, mijia.DeviceSelector{DID: "设备 DID"})
// 也可以按名称选择:mijia.DeviceSelector{Name: "客厅灯"}
if err != nil {
	return err
}

value, err := device.Get(ctx, "on")
if err != nil {
	return err
}

if err := device.Set(ctx, "on", true); err != nil {
	return err
}

_, err = device.RunAction(ctx, "toggle", nil)
_ = value

可通过实际导出的 DeviceOption 调整行为:

device, err := mijia.NewDevice(
	ctx,
	client,
	mijia.DeviceSelector{Name: "客厅灯"},
	mijia.WithDeviceDelay(0),
	mijia.WithDeviceCacheDir("./miot-cache"),
	mijia.WithDeviceHTTPClient(http.DefaultClient),
)

超时与错误

所有网络和设备操作都接收 context.Context,建议设置超时:

ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()

devices, err := client.GetDevices(ctx, "")

API 错误和高级设备错误可使用 errors.As 判断:

var apiErr *mijia.APIError
var deviceErr *mijia.DeviceGetError

switch {
case errors.As(err, &apiErr):
	log.Printf("API 错误: code=%d message=%s", apiErr.Code, apiErr.Message)
case errors.As(err, &deviceErr):
	log.Printf("设备读取错误: device=%s property=%s code=%d", deviceErr.DeviceName, deviceErr.Name, deviceErr.Code)
}

写属性和执行 action 时,对应错误类型为 DeviceSetErrorDeviceActionError;设备选择还可能返回 DeviceNotFoundErrorMultipleDevicesFoundError

S
Description
No description provided
Readme
413 KiB
Languages
Go 100%