Files
xiaomihome/bridge/bridge.go
T
4566704 2b7de69403 refactor(logger): 日志改用 logrus 全局包级调用,移除 Logger 注入接口
为什么:并入宿主项目后须与宿主日志体系一致——宿主统一用 pkg/logger 初始化
logrus 全局实例并直接 logrus.Xxx 包级调用;包一层 Logger 接口会让 logrus 的
caller 定位(报告调用文件:行号)失准。

改动:
- 删除 logger 包(Logger 接口 / Default / SetDefault,默认本就是 logrus.StandardLogger)
- 11 个结构体移除 lgr logger.Logger 字段、SetLogger 方法及构造中的 lgr 初始化
- 192 处 lgr.Xxxf 调用改为 logrus.Xxxf,日志消息文本保持不变
- xiaomi.Client 移除 Logger() 访问器,air_conditioner 回读日志去掉判空调用直连 logrus
- miot_i18n.go 两处标准库 log.Printf 改为 logrus.Errorf,统一日志出口
- go.mod:logrus 从 indirect 提升为直接依赖
- goimports/goformat 全树规范化:此前部分文件未 gofmt(单行 if、对齐),本次顺带
  格式化,纯空白/换行变化,无逻辑改动

验证:go build / go vet / go test ./... 全部通过(bridge、miot、xiaomi、devices、specs)
2026-09-15 20:16:26 +08:00

359 lines
9.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package bridge 提供多用户小米设备管理门面。
//
// 外部只需 import 这一个包,无需直接依赖 xiaomi/devices/miot。
//
// 用法:
//
// pool := bridge.NewPool()
// pool.Add(ctx, id, uuid, auth, dataDir)
// b, _ := pool.Get(id)
// ac, _ := b.AC("did")
// ac.TurnOn(ctx)
// state := ac.GetProps()
package bridge
import (
"context"
"fmt"
"sync"
"xiaomihome/xiaomi"
"xiaomihome/xiaomi/devices"
"github.com/sirupsen/logrus"
)
// ── 类型别名:业务层不需要 import devices ──
type (
ACProps = devices.ACProps
LightProps = devices.LightProps
FanProps = devices.FanProps
HumidifierProps = devices.HumidifierProps
HeaterProps = devices.HeaterProps
VacuumProps = devices.VacuumProps
SwitchProps = devices.SwitchProps
SpeakerProps = devices.SpeakerProps
PlugProps = devices.PlugProps
)
// OccupancyProps 人体传感器状态(有人/无人)。
type OccupancyProps struct {
Occupied bool `json:"occupied"`
}
// ── Pool ──
// Pool 全局路由:id → Bridge。
type Pool struct {
mu sync.RWMutex
items map[int64]*Bridge
readbackModels []string // 需要 set 后回读的型号列表(透传到 xiaomi.Config)
}
// NewPool 创建 Pool。readbackModels 为需要"set 后回读"的设备型号列表(可选,默认空)。
func NewPool(readbackModels ...[]string) *Pool {
var models []string
if len(readbackModels) > 0 {
models = readbackModels[0]
}
return &Pool{items: make(map[int64]*Bridge), readbackModels: models}
}
// Get 按 id 查找。
func (p *Pool) Get(id int64) (*Bridge, bool) {
p.mu.RLock()
defer p.mu.RUnlock()
b, ok := p.items[id]
return b, ok
}
// Add 新建并注册一个 Bridge。
func (p *Pool) Add(ctx context.Context, id int64, uuid string, auth xiaomi.AuthInfo, dataDir string, homeIDs []int64) (*Bridge, error) {
p.mu.Lock()
defer p.mu.Unlock()
if _, exists := p.items[id]; exists {
return nil, fmt.Errorf("bridge: %d already exists", id)
}
b, err := newBridge(ctx, id, uuid, auth, dataDir, homeIDs, p.readbackModels)
if err != nil {
return nil, err
}
p.items[id] = b
return b, nil
}
// Remove 移除并关闭。
func (p *Pool) Remove(id int64) error {
p.mu.Lock()
b, ok := p.items[id]
if ok {
delete(p.items, id)
}
p.mu.Unlock()
if !ok {
return fmt.Errorf("bridge: %d not found", id)
}
return b.Close()
}
// Close 关闭所有。
func (p *Pool) Close() {
p.mu.Lock()
defer p.mu.Unlock()
for id, b := range p.items {
b.Close()
delete(p.items, id)
}
}
// ── Bridge ──
// Bridge 封装单个用户的 Client + 类型化设备控制器 + 状态缓存。
type Bridge struct {
id int64
client *xiaomi.Client
mu sync.RWMutex
dv map[string]interface{} // DID → devices.Xxx interface
onPropsChanged func(id int64, did, deviceType, changed string, props interface{})
onOnlineChanged func(id int64, did string, online bool)
}
func newBridge(ctx context.Context, id int64, uuid string, auth xiaomi.AuthInfo, dataDir string, homeIDs []int64, readbackModels []string) (*Bridge, error) {
idStr := fmt.Sprintf("%d", id)
client, err := xiaomi.NewClient(ctx, xiaomi.Config{
AuthInfo: auth,
DataDir: dataDir,
UUID: uuid,
UID: idStr,
HomeIDs: homeIDs,
ReadbackModels: readbackModels,
})
if err != nil {
return nil, fmt.Errorf("bridge: %w", err)
}
b := &Bridge{
id: id,
client: client,
dv: make(map[string]interface{}),
}
devList, err := client.GetDevices(ctx)
if err != nil {
client.Close()
return nil, fmt.Errorf("bridge: GetDevices: %w", err)
}
for _, info := range devList {
d, createErr := devices.Create(client, info)
if createErr != nil || d == nil {
if createErr != nil {
logrus.Warnf("[bridge] %d: create device %s (%s) failed: %v", id, info.DID, info.Model, createErr)
} else {
logrus.Debugf("[bridge] %d: device %s (%s) unsupported, skipped", id, info.DID, info.Model)
}
continue
}
b.dv[info.DID] = d
}
logrus.Infof("[bridge] %d: %d devices", id, len(b.dv))
return b, nil
}
func (b *Bridge) Close() error { return b.client.Close() }
// Devices 返回设备列表(DID → 类型名)。
func (b *Bridge) Devices() map[string]string {
b.mu.RLock()
defer b.mu.RUnlock()
out := make(map[string]string, len(b.dv))
for did, d := range b.dv {
out[did] = deviceTypeName(d)
}
return out
}
// pruneControllers 从本地控制器集合中剔除「米家已不存在」的设备,返回被剔除的 DID。
// 没有这一步,AC()/Switch()/Fan() 等访问器会继续为已删设备返回控制器,控制必然失败。
func pruneControllers(dv map[string]interface{}, alive map[string]struct{}) []string {
var removed []string
for did := range dv {
if _, ok := alive[did]; !ok {
delete(dv, did)
removed = append(removed, did)
}
}
return removed
}
func deviceTypeName(d interface{}) string {
switch d.(type) {
case devices.AirConditioner:
return devices.TypeAirConditioner
case devices.Light:
return devices.TypeLight
case devices.Switch:
return devices.TypeSwitch
case devices.Fan:
return devices.TypeFan
case devices.Cover:
return devices.TypeCover
case devices.Humidifier:
return devices.TypeHumidifier
case devices.Vacuum:
return devices.TypeVacuum
case devices.WaterHeater:
return devices.TypeWaterHeater
case devices.TempHumiditySensor:
return devices.TypeTempHumidSensor
case devices.OccupancySensor:
return devices.TypeOccupancySensor
case devices.Speaker:
return devices.TypeSpeaker
case devices.Gateway:
return devices.TypeGateway
case devices.Plug:
return devices.TypePlug
default:
return "unknown"
}
}
// ── 类型化设备访问器 ──
func (b *Bridge) AC(did string) (devices.AirConditioner, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
ac, ok := d.(devices.AirConditioner)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not an AirConditioner (got %T)", did, d)
}
return ac, nil
}
func (b *Bridge) Light(did string) (devices.Light, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
lt, ok := d.(devices.Light)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a Light", did)
}
return lt, nil
}
func (b *Bridge) Switch(did string) (devices.Switch, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
sw, ok := d.(devices.Switch)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a Switch", did)
}
return sw, nil
}
func (b *Bridge) Fan(did string) (devices.Fan, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
f, ok := d.(devices.Fan)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a Fan", did)
}
return f, nil
}
func (b *Bridge) Cover(did string) (devices.Cover, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
cv, ok := d.(devices.Cover)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a Cover", did)
}
return cv, nil
}
func (b *Bridge) Humidifier(did string) (devices.Humidifier, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
h, ok := d.(devices.Humidifier)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a Humidifier", did)
}
return h, nil
}
func (b *Bridge) Vacuum(did string) (devices.Vacuum, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
v, ok := d.(devices.Vacuum)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a Vacuum", did)
}
return v, nil
}
func (b *Bridge) WaterHeater(did string) (devices.WaterHeater, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
wh, ok := d.(devices.WaterHeater)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a WaterHeater", did)
}
return wh, nil
}
func (b *Bridge) TempHumidity(did string) (devices.TempHumiditySensor, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
s, ok := d.(devices.TempHumiditySensor)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a TempHumiditySensor", did)
}
return s, nil
}
func (b *Bridge) Occupancy(did string) (devices.OccupancySensor, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
s, ok := d.(devices.OccupancySensor)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not an OccupancySensor", did)
}
return s, nil
}
func (b *Bridge) Plug(did string) (devices.Plug, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
pg, ok := d.(devices.Plug)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a Plug", did)
}
return pg, nil
}
func (b *Bridge) Speaker(did string) (devices.Speaker, error) {
d, ok := b.dv[did]
if !ok {
return nil, fmt.Errorf("bridge: device %s not found", did)
}
sp, ok := d.(devices.Speaker)
if !ok {
return nil, fmt.Errorf("bridge: device %s is not a Speaker", did)
}
return sp, nil
}