// 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/logger" "xiaomihome/xiaomi" "xiaomihome/xiaomi/devices" ) // ── 类型别名:业务层不需要 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 lgr logger.Logger 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, lgr: logger.Default(), 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 { b.lgr.Warnf("[bridge] %d: create device %s (%s) failed: %v", id, info.DID, info.Model, createErr) } else { b.lgr.Debugf("[bridge] %d: device %s (%s) unsupported, skipped", id, info.DID, info.Model) } continue } b.dv[info.DID] = d } b.lgr.Infof("[bridge] %d: %d devices", id, len(b.dv)) return b, nil } // SetLogger 设置日志记录器。 func (b *Bridge) SetLogger(l logger.Logger) { b.lgr = l } 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 } 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 }