refactor: 重大架构升级 — HomeID/RoomID int64化 + 设备驱动重构 + 结构化日志

主要变更:

**类型系统重构**
- HomeInfo.ID/RoomInfo.ID/HomeID 从 string 改为 int64,json tag 使用 ,string
- GetHome/GetRooms/GetRoomDevices 接口同步修改
- 新增 int64FromStr 工具函数,更新 parseHomeInfo 解析逻辑

**新增 Speaker 设备**
- 新增 KindSpeaker 类型
- classify 映射覆盖 speaker/bluetooth-speaker/alarm-clock/media-player/playback/music

**设备驱动重构**
- AC 驱动彻底重写:从硬编码常量改为基于 SPEC ValueList 的动态 valMapper
- 所有设备(Fan/Humidifier/Light/OccupancySensor/Switch/TempHumiditySensor/Thermostat/Vacuum/WaterHeater/Cover)新增 GetState() 和 FetchState(ctx) 接口
- base.go 新增 specResolver.format() 方法

**MIoT 模块增强**
- MIoTClient 新增 homeIDs 字段,支持按家庭过滤设备
- RefreshDevices 支持 homeIDs 参数
- SpecStdLib 引入 logger.Logger,log.Printf 全部迁移为结构化日志

**其他**
- Config 新增 RedirectURL 和 HomeIDs 字段
- AuthInfo.ExpiresIn 类型从 int 改为 int32
- UUID 生成简化为 hex.EncodeToString
- 新增 github.com/sirupsen/logrus 依赖
- .gitignore 新增 examples/*/data/ 忽略规则
- 删除已提交的 config.json 敏感配置
This commit is contained in:
2026-07-13 10:40:20 +08:00
parent 8ffc9e4e44
commit b808e51b3e
68 changed files with 2506 additions and 688 deletions
+25 -11
View File
@@ -6,6 +6,7 @@ import (
"sync"
"time"
"xiaomihome/logger"
"xiaomihome/miot"
)
@@ -17,6 +18,7 @@ type Client struct {
mipsCloud *miot.MipsCloudClient
specParser *miot.MIoTSpecParser
uuid string
lgr logger.Logger
deviceMu sync.RWMutex
devices map[string]*miot.MIoTDevice
@@ -70,35 +72,47 @@ func NewClient(ctx context.Context, cfg Config) (*Client, error) {
)
miotClient.SetMipsCloud(mipsCloud)
var ids []string
if len(cfg.HomeIDs) > 0 {
ids = make([]string, len(cfg.HomeIDs))
for i, v := range cfg.HomeIDs {
ids[i] = fmt.Sprintf("%d", v)
}
miotClient.SetHomeIDs(ids)
}
if err := miotClient.Init(); err != nil {
return nil, fmt.Errorf("xiaomi: init: %w", err)
}
_ = miotClient.RefreshDevices()
// Use the same homeIDs slice from above (nil if not set)
_ = miotClient.RefreshDevices(ids)
if err := mipsCloud.Connect(); err != nil {
return nil, fmt.Errorf("xiaomi: mips connect: %w", err)
}
// 启动后台 token 自动刷新
if cfg.OnTokenRefreshed != nil {
miotClient.SetTokenRefreshCallback(cfg.OnTokenRefreshed)
}
miotClient.RequestRefreshAuthInfo(10)
return &Client{
c := &Client{
cfg: cfg,
inner: miotClient,
http: httpClient,
mipsCloud: mipsCloud,
uuid: uuid,
lgr: logger.Default(),
specParser: miot.NewMIoTSpecParser("en", miot.NewMIoTStorage(cfg.DataDir), cfg.DataDir+"/specs"),
devices: make(map[string]*miot.MIoTDevice),
propSubs: make(map[string]string),
eventSubs: make(map[string]string),
}, nil
}
return c, nil
}
// SetLogger 设置日志记录器。
func (c *Client) SetLogger(l logger.Logger) { c.lgr = l }
// SetHomeIDs 动态设置 miot 层的家庭过滤,供后台定时 RefreshDevices 使用。
func (c *Client) SetHomeIDs(homeIDs []string) { c.inner.SetHomeIDs(homeIDs) }
// Close 关闭客户端。
func (c *Client) Close() error {
if c.mipsCloud != nil {
@@ -119,7 +133,7 @@ func (c *Client) RefreshToken(ctx context.Context) (*TokenResult, error) {
return &TokenResult{
AccessToken: strVal(result, "access_token"),
RefreshToken: strVal(result, "refresh_token"),
ExpiresIn: intVal(result, "expires_in"),
ExpiresIn: int32(intVal(result, "expires_in")),
ExpiresTS: int64(intVal(result, "expires_ts")),
UID: strVal(result, "uid"),
}, nil
@@ -140,7 +154,7 @@ func (c *Client) getOrCreateDevice(did string, info map[string]interface{}) *mio
if err == nil {
spec = s
} else {
fmt.Printf("[xiaomi] spec parse failed for %s (%s): %v\n", did, urn, err)
c.lgr.Warnf("[xiaomi] spec parse failed for %s (%s): %v", did, urn, err)
}
}